Безопасные куки в сессиях PHP - PullRequest
0 голосов
/ 11 октября 2010

Я разработал класс сеанса PHP и протестировал его на нескольких примерах (см. Исходный код ниже). Кажется, все в порядке. Теперь я хотел бы сделать этот сеанс как-то «безопасным». Я нашел некоторый пример кода, который предназначен для шифрования куки (в книге Courioso Expert PHP и MySQL). Вот этот фрагмент кода.

Код для зашифрованного куки

$cookieData = serialize($user);

$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_CBC);
srand();
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
$encryptedData = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $secret, 
            $cookieData, MCRYPT_MODE_CBC, $iv);
setcookie('user', base64_encode).':'.$iv);

и аналогичный код для расшифровки куки

Мой первый вопрос: могу ли я доверять этому коду. То есть это действительно безопасно? (Я попробовал некоторые другие примеры кода этой книги и нашел их довольно глючными).

Я не до конца понимаю, как код для шифрования куки может быть встроен в мой класс сеанса (либо код выше - или любой другой код шифрования куки). Должен ли я заменить файл cookie, который автоматически генерируется PHP (PHPSESSID), или создать новый файл cookie и зашифровать этот специальный файл cookie? (Мое предположение второе верно, но я хотел бы быть уверен).

Как работает защита через зашифрованные куки? Я представляю следующую атаку: (1) если злоумышленник каким-то образом получает печенье в своей руке, он просто отвечает тем же cookie. Хорошо. у него нет пароля, но у него есть зашифрованный пароль. Позже я проверяю только в порядке ли зашифрованный пароль. Так что, если у злоумышленника есть cookie: игра окончена - независимо от того, содержит ли cookie пароль или «только» зашифрованный пароль. Правильный? (2) Хорошо, эту проблему можно решить с помощью зашифрованной передачи, такой как SSL. Но тогда я думаю, что если я использую SLL, то передача в любом случае зашифрована. Мне не нужно шифровать пароль второй раз, используя функцию шифрования. Правильно?

Вот код для класса сеанса, который у меня уже есть.

Спасибо

<?php

class SessionClass {

    private static $_instance;
    public static function getInstance() 
    {
        if (!(self::$_instance instanceof self))
        {
            self::$_instance = new self();
        }
        return self::$_instance;
    } // getInstance


    public function __construct()
    {
        session_set_save_handler(
            array($this, "open"), array($this, "close"),
            array($this, "read"), array($this, "write"),
            array($this, "destroy"), array($this, "gc")
        );

        $createTable = "CREATE TABLE IF NOT EXISTS `session`( ".
                            "`sessionID` VARCHAR(128), ".
                            "`data` MEDIUMBLOB, ".
                            "`timestamp` INT, ".
                            "`ip` VARCHAR(15), ".
                            "PRIMARY KEY (`sessionID` ), ".
                            "KEY (`timestamp`, `sessionID`))";

        mysql_query($createTable);
    } // construct


    public function __destruct() {
        session_write_close();
    }

    public function open ($path, $id) {
        // do nothing
        return (true);
    }

    public function close() {
        // do nothing
        return (true);
    }

    public function read($id) 
    {
        $escapedID = mysql_escape_string($id);
        $query = sprintf("SELECT * FROM session WHERE sessionID = '%s'", $escapedID);
        $res = mysql_query($query);

        if ((!$res) || (!mysql_num_rows($res))) {
            $timestamp = time();
            $query = sprintf("INSERT INTO session (sessionID, timestamp) VALUES ('%s', %s)", $escapedID, $timestamp);
            mysql_query($query);
            return '';
        } elseif (($row = mysql_fetch_assoc($res))) {
            $query = "UPDATE session SET timestamp = ";
            $query .= time();
            $query .= sprintf (" WHERE sessionID = '%s'", $escapedID);
            mysql_query($query);
            return $row['data'];
        } // elseif

        return "";
    } // read


    public function write($id, $data)
    {
        $query = "REPLACE INTO session (sessionID, data, ip, timestamp) ";
        $query .= sprintf("VALUES ('%s', '%s', '%s', %s)", 
            mysql_escape_string($id), mysql_escape_string($data),
            $_SERVER['REMOTE_ADDR'], time());
        mysql_query($query);
        return (true);
    } // write


    public function destroy($id)
    {
        $escapedID = mysql_escape_string($id);
        $query = sprintf("DELETE FROM session WHERE sessionID = %s", $escapedID);
        $res = mysql_query($query);
        return (mysql_affected_rows($res) == 1);
    } // destroy


    public function gc($lifetime)
    {
        $query = "DELETE FROM session WHERE ";
        $query = sprintf("%s - timestamp > %s", time(), $lifetime);
        mysql_query($query);
        return (true);
    } // gc

} // SessionClass

Ответы [ 2 ]

2 голосов
/ 11 октября 2010

Причиной использования сеансов является не для хранения конфиденциальных данных на стороне клиента, а для сохранения их на стороне сервера. И единственное соединение с этими данными - это идентификатор сеанса, который, как мы надеемся, периодически меняется с каждой аутентификацией и изменением авторизации.

Если вы теперь хотите сохранить эти данные на стороне клиента (независимо от того, в какой форме или представлении), вы сделали бы противоположное тому, для чего предназначены сеансы. Поэтому не делайте этого и храните ваши конфиденциальные данные на стороне сервера.

1 голос
/ 11 октября 2010

Если я могу отступить от вашего первоначального вопроса. Вы должны не хранить пароли в куки. Cookie хранится на стороне клиента. Независимо от того, какое шифрование используется, гораздо более безопасный вариант - сохранить ваши данные аутентификации пользователя в сеансе и просто сохранить токен в файле cookie на стороне клиента, который вызовет соответствующий сеанс.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...