Хорошо, есть две отдельные, но связанные проблемы, и каждая решается по-своему.
Фиксация сессии
Здесь злоумышленник явно устанавливает идентификатор сеанса для пользователя. Обычно в PHP это делается путем предоставления им URL-адреса http://www.example.com/index...?session_name=sessionid
. Как только злоумышленник передает URL-адрес клиенту, атака аналогична атаке захвата сеанса.
Есть несколько способов предотвратить фиксацию сеанса (сделать все из них):
Установите session.use_trans_sid = 0
в своем php.ini
файле. Это скажет PHP не включать идентификатор в URL и не читать URL для идентификаторов.
Установите session.use_only_cookies = 1
в своем php.ini
файле. Это скажет PHP никогда не использовать URL с идентификаторами сеанса.
Регенерируйте идентификатор сеанса каждый раз, когда изменяется статус сеанса. Это означает любое из следующего:
- Аутентификация пользователя
- Хранение конфиденциальной информации в сеансе
- Менять что-либо в сеансе
- и т.д...
Session Hijacking
Именно здесь злоумышленник получает идентификатор сеанса и может отправлять запросы, как если бы они были этим пользователем. Это означает, что, поскольку у злоумышленника есть идентификатор, он практически неотличим от действительного пользователя по отношению к серверу.
Вы не можете напрямую предотвратить угон сеанса. Однако вы можете сделать шаги, чтобы сделать его очень сложным и сложным в использовании.
Используйте сильный идентификатор хеша сеанса: session.hash_function
in php.ini
. Если PHP <5.3, установите его session.hash_function = 1
для SHA1. Если PHP> = 5.3, установите его на session.hash_function = sha256
или session.hash_function = sha512
.
Отправить сильный хеш: session.hash_bits_per_character
в php.ini
. Установите это в session.hash_bits_per_character = 5
. Хотя это не усложняет взлом, но имеет значение, когда злоумышленник пытается угадать идентификатор сеанса. Идентификатор будет короче, но использует больше символов.
Установите дополнительную энтропию с session.entropy_file
и session.entropy_length
в вашем php.ini
файле. session.entropy_file = /dev/urandom
Например, установите для первого значение, а для второго - количество байтов, которые будут считываться из файла энтропии session.entropy_length = 256
.
Измените имя сеанса с PHPSESSID по умолчанию. Это достигается путем вызова session_name()
с вашим собственным идентификатором в качестве первого параметра перед вызовом session_start
.
Если вы действительно параноик, вы также можете повернуть имя сеанса, но помните, что все сеансы будут автоматически признаны недействительными, если вы измените это (например, если вы сделаете его зависимым от времени). Но в зависимости от вашего варианта использования, это может быть вариант ...
Часто меняйте идентификатор сессии. Я не буду делать это каждый запрос (если только вам действительно не нужен этот уровень безопасности), но с произвольным интервалом. Вы хотите часто менять это, поскольку если злоумышленник захватывает сеанс, вы не хотите, чтобы он мог использовать его слишком долго.
Включите пользовательский агент из$_SERVER['HTTP_USER_AGENT']
сеанса. Обычно, когда начинается сессия, сохраняйте ее как-то так $_SESSION['user_agent']
. Затем при каждом последующем запросе проверяйте, соответствует ли он. Обратите внимание, что это может быть подделано, так что это не на 100% надежно, но лучше, чем нет.
Включите IP-адрес пользователя$_SERVER['REMOTE_ADDR']
в сеансе. Обычно, когда начинается сессия, сохраняйте ее как-то так $_SESSION['remote_ip']
. Это может быть проблематично для некоторых интернет-провайдеров, которые используют несколько IP-адресов для своих пользователей (например, AOL раньше). Но если вы используете его, это будет гораздо более безопасным. Для злоумышленника единственный способ подделать IP-адрес - это скомпрометировать сеть в некоторый момент между реальным пользователем и вами. И если они скомпрометируют сеть, они могут сделать намного хуже, чем угон (например, атаки MITM и т. Д.).
Включите токен в сеанс и на стороне браузера, который вы увеличиваете и часто сравниваете. В основном для каждого запроса делайте $_SESSION['counter']++
на стороне сервера. Также сделайте что-нибудь в JS на стороне браузера, чтобы сделать то же самое (используя локальное хранилище). Затем, когда вы отправляете запрос, просто возьмите одноразовый номер токена и убедитесь, что на сервере совпадает одноразовый номер. Сделав это, вы сможете обнаружить захваченный сеанс, поскольку у злоумышленника не будет точного счетчика, или, если он это сделает, у вас будет 2 системы, передающие одинаковое количество, и они могут сказать, что одна из них подделана. Это не будет работать для всех приложений, но это один из способов борьбы с проблемой.
Записка о двух
Разница между фиксацией сеанса и перехватом заключается только в том, как скомпрометирован идентификатор сеанса. В фиксации для идентификатора устанавливается значение, которое злоумышленник знает заранее. В угоне это либо угадано, либо украдено у пользователя. В противном случае эффекты двух одинаковы, когда идентификатор скомпрометирован.
Сессия ID Регенерация
Всякий раз, когда вы восстанавливаете идентификатор сеанса, используя session_regenerate_id
старый сеанс, его следует удалить. Это происходит прозрачно с основным обработчиком сеанса. Однако некоторые пользовательские обработчики сессийsession_set_save_handler()
не делают этого и открыты для атаки на старые идентификаторы сессий. Убедитесь, что, если вы используете пользовательский обработчик сеанса, что вы отслеживаете открываемый идентификатор, и если он не тот, который вы сохраняете, вы явно удаляете (или меняете) идентификатор старого.
Используя обработчик сеанса по умолчанию, вам достаточно просто позвонить session_regenerate_id(true)
. Это удалит старую информацию о сеансе для вас. Старый идентификатор больше не действителен и приведет к созданию нового сеанса, если злоумышленник (или кто-либо еще в этом отношении) попытается использовать его. Будьте осторожны с пользовательскими обработчиками сессий, хотя ....
Уничтожение сессии
Если вы собираетесь уничтожить сеанс (например, при выходе из системы), убедитесь, что вы уничтожили его полностью. Это включает в себя удаление куки. Использование session_destroy
:
function destroySession() {
$params = session_get_cookie_params();
setcookie(session_name(), '', time() - 42000,
$params["path"], $params["domain"],
$params["secure"], $params["httponly"]
);
session_destroy();
}