Я разработал класс сеанса PHP и протестировал его с помощью нескольких примеров (см. Исходный код ниже). Кажется, все в порядке. Теперь я хотел бы сделать этот сеанс «безопасным». Я нашел пример кода, который предназначен для шифрования файла cookie (в книге Courioso Expert PHP и MySQL). Вот этот фрагмент кода.Безопасные файлы cookie на сеансах PHP
Код для зашифрованного печенья
$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 может быть встроен в мой класс сеанса (либо код выше, либо любой другой код шифрования файлов cookie). Должен ли я заменить файл cookie, который автоматически генерируется PHP (PHPSESSID), или должен генерировать новый файл cookie и шифровать этот настраиваемый файл cookie? (Мое предположение - второе, правда, но я хотел бы быть уверенным).
Как защищается с помощью зашифрованных файлов cookie? Я предполагаю следующую атаку: (1), если злоумышленник каким-то образом получит куки-файл в своей руке, он просто отвечает тем же файлом-печеньем. Хорошо. у него нет пароля, но у него есть зашифрованный пароль. Позже я проверяю, только если зашифрованный пароль в порядке. Так что если у атакующего есть файл 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
Вы сохраняете 'sessionID' в базе данных, это можно получить с помощью sql-инъекции и использовать для входа в систему, таким образом побеждая цель хэширования пароля. – rook
Также IP-адрес может меняться во время сеанса, если они находятся за балансировщиком нагрузки. Например, если они находятся в школьной или крупной корпоративной сети. – rook