Исправление / взлом сеанса PHP

Я пытаюсь больше узнать о фиксации сеанса и угоне PHP, а также о том, как предотвратить эти проблемы. Я читал следующие две статьи на сайте Криса Шифлетта:

Однако я не уверен, что правильно понимаю.

Достаточно ли для предотвращения фиксации сеанса связи позвонить session_regenerate_id(true); после успешного входа в систему? Думаю, я правильно это понимаю.

Он также говорит об использовании токенов, передаваемых в URL-адресах через $_GET, для предотвращения перехвата сеанса. Как именно это сделать? Я предполагаю, что когда кто-то входит в систему, вы генерируете их токен и сохраняете его в переменной сеанса, а затем на каждой странице вы сравниваете эту переменную сеанса со значением переменной $_GET?

Нужно ли менять этот токен только один раз за сеанс или при каждой загрузке страницы?

Также есть хороший способ предотвратить угон без необходимости передавать значение в URL-адресах? Это было бы намного проще.


person me2    schedule 22.02.2011    source источник
comment
Возможно, вы могли бы добавить ссылки на страницы, на которых вы нашли эти рекомендации.   -  person Gumbo    schedule 22.02.2011


Ответы (5)


Хорошо, есть две отдельные, но связанные проблемы, и каждая решается по-своему.

Фиксация сеанса

Здесь злоумышленник явно устанавливает идентификатор сеанса для пользователя. Обычно в 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-адреса с идентификаторами сеанса.

  • Повторно генерируйте идентификатор сеанса в любое время при изменении статуса сеанса. Это означает любое из следующего:

    • User authentication
    • Хранение конфиденциальной информации в сеансе
    • Изменение чего-либо в сеансе
    • и т.д...

Взлом сеанса

Здесь злоумышленник получает идентификатор сеанса и может отправлять запросы, как если бы он был этим пользователем. Это означает, что, поскольку злоумышленник имеет идентификатор, они практически неотличимы от действительного пользователя по отношению к серверу.

Вы не можете напрямую предотвратить захват сеанса. Однако вы можете предпринять шаги, чтобы сделать его очень трудным и трудным в использовании.

  • Используйте надежный хэш-идентификатор сеанса: session.hash_function в 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 на стороне браузера, чтобы сделать то же самое (используя локальное хранилище). Затем, когда вы отправляете запрос, просто возьмите одноразовый номер токена и убедитесь, что одноразовый номер такой же на сервере. Сделав это, вы сможете обнаружить захваченный сеанс, поскольку у злоумышленника не будет точного счетчика, или, если они есть, у вас будет две системы, передающие один и тот же счетчик, и могут сказать, что одна из них подделана. Это работает не для всех приложений, но является одним из способов решения проблемы.

Примечание о двух

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

Восстановление идентификатора сеанса

Каждый раз при повторном создании идентификатора сеанса с помощью session_regenerate_id старый сеанс следует удалить. Это происходит прозрачно с основным обработчиком сеанса. Однако некоторые пользовательские обработчики сеансов, использующие session_set_save_handler(), не делают этого и открыты. атаковать старые идентификаторы сессий. Убедитесь, что если вы используете настраиваемый обработчик сеанса, вы отслеживаете идентификатор, который вы открываете, а если он не тот, который вы сохраняете, вы явно удаляете (или меняете) идентификатор на старом.

Используя обработчик сеанса по умолчанию, вы можете просто вызвать session_regenerate_id(true). Это удалит старую информацию о сеансе. Старый идентификатор больше недействителен и приведет к созданию нового сеанса, если злоумышленник (или кто-либо другой в этом отношении) попытается его использовать. Однако будьте осторожны с пользовательскими обработчиками сеансов ....

Уничтожение сеанса

Если вы собираетесь уничтожить сеанс (например, при выходе из системы), убедитесь, что вы уничтожили его полностью. Это включает в себя отключение файла cookie. Используя session_destroy:

function destroySession() {
    $params = session_get_cookie_params();
    setcookie(session_name(), '', time() - 42000,
        $params["path"], $params["domain"],
        $params["secure"], $params["httponly"]
    );
    session_destroy();
}
person ircmaxell    schedule 22.02.2011
comment
Использование 5 вместо 4 бит на символ никоим образом не меняет «силу» (что бы в данном случае ни означало «сила»). Но хотя ваши замечания в целом целесообразны, в них отсутствуют некоторые важные детали. Например, что происходит с сеансом, связанным со старым идентификатором сеанса, или как следует обрабатывать сеанс со старым идентификатором сеанса после того, как он стал недействительным. - person Gumbo; 22.02.2011
comment
@Gumbo Не удалось получить ваше последнее предложение полностью, но не будет ли старый сеанс перенесен на новый в случае регенерации идентификатора; и в чем проблема с недействительной сессией? - person Halil Özgür; 22.02.2011
comment
@battal: Нет, в том-то и дело. session_regenerate_id не делает недействительным сеанс, который все еще связан со старым идентификатором; только если для параметра delete_old_session установлено значение true, сеанс будет уничтожен. Но что, если злоумышленник инициировал регенерацию этого идентификатора? - person Gumbo; 22.02.2011
comment
Я не согласен с регенерацией сеанса каждый раз, когда вы меняете переменную сеанса, это должно выполняться только при входе в систему / выходе из системы. Также проверка пользовательского агента бессмысленна, а проверка REMOTE_ADDR проблематична. Одна вещь, которую я хотел бы добавить, - это session.entropy_file = /dev/urandom. Доказано, что внутренняя генерация энтропии PHP является чрезвычайно слабой, а пул энтропии, предоставляемый / dev / random или / dev / uranom, - лучшее, что вы можете получить на веб-сервере без аппаратного rng. - person rook; 22.02.2011
comment
Также вы должны добавить session.cookie_httponly и session.cookie_secure. Первый помогает помешать xss (но не идеально). Второй - лучший способ остановить OWASP A9 ... - person rook; 22.02.2011
comment
@Gumbo А, я вижу, это сохраняется при просмотре страницы. Это похоже на важные моменты для любого нетривиального приложения. Интересное преобразование в разделе комментариев на странице руководства session_regenerate_id. Что вы думаете об этом, особенно о комментарии 87905 ? - person Halil Özgür; 22.02.2011
comment
@ircmaxell Хотя это не усложняет взлом, все же имеет значение, когда злоумышленник пытается угадать идентификатор сеанса - какая разница с точки зрения безопасности, если хеш-энтропия такая же? И к началу предложения Отправить сильный хеш - я думаю, что это не имеет ничего общего с силой хеша, только его читаемое представление. - person Halil Özgür; 22.02.2011
comment
@battal: я не понимаю, почему старый идентификатор сеанса и, следовательно, старый сеанс должен быть действителен еще 60 секунд. Это все равно, что иметь защитную дверь, которая автоматически закрывается не раньше чем через 60 секунд после того, как вы проходите мимо, и блокирует всех, кто проходит после вас. Это имело бы смысл использовать его только для обнаружения злоумышленника (в случае перехвата сеанса) или жертвы (в случае фиксации сеанса). Но вы не можете точно сказать, кто есть кто. - person Gumbo; 22.02.2011
comment
@Gumbo: решение в этих комментариях пытается передать старый сеанс обратно уже запущенным сценариям (обычно они не могут получить к нему доступ, пока сценарий, выполняющий идентификатор регенерации, не завершится). И раз уж они запустились до смены сеанса, стоит ли называть это уязвимостью? Разве не было бы двусмысленным запускать половину скрипта с одним сеансом, а другую половину с другим сеансом (кроме push-вещей и т. Д.)? - person Halil Özgür; 23.02.2011
comment
@battal: Нет, это новый идентификатор сеанса, который используется после восстановления идентификатора, а данные сеанса в $_SESSION остаются неизменными. Но, честно говоря, вызов session_regenerate_id не записывает данные сеанса $_SESSION обратно в хранилище. Таким образом, локальное изменение $_SESSION['OBSOLETE'] и $_SESSION['EXPIRES'] изменяет только данные текущего сеанса с новым сгенерированным идентификатором, но не со старым. Чтобы установить эти значения для старого сеанса, вам лучше вызвать session_write_close до session_regenerate_id. - person Gumbo; 23.02.2011
comment
Вы также можете изменить имя сеанса по умолчанию (PHPSESSID), используя: session.name = blah вместо вызова session_name() - person Mike Causer; 17.08.2012
comment
@MikeCauser Но использование session_name() более переносимо; представьте серверы, которые не предоставляют доступ к php.ini или .htacess. - person Ja͢ck; 01.12.2012
comment
Более портативный да, но кучи медленнее. Я мог понять, что php.ini не отображается в общей среде, но наверняка .htaccess будет доступен - person Mike Causer; 03.12.2012
comment
@MikeCauser: медленнее? Действительно? Вы собираетесь привести этот аргумент? Мы говорим о буквально микросекундах. И так как он в любом случае никогда не будет зацикливаться (не должно, если вы не делаете что-то ужасно неправильно). Я бы использовал session_name() просто потому, что он сохраняет конфигурацию приложения в приложении. - person ircmaxell; 03.12.2012
comment
Не понимаю такого отличного ответа, но упускает самую важную вещь: используйте SSL / HTTPS. Увеличение счетчика является источником проблемы, когда несколько запросов быстро следуют друг за другом, пользователь дважды обновляет страницу или дважды нажимает кнопку отправки. Решение IP-адреса является проблемой в настоящее время для всех мобильных пользователей и когда-либо меняющих IP-адреса. Вы можете посмотреть на первый набор IP, но он все равно вызывает проблемы. Лучше всего предотвращает обнаружение идентификатора сеанса в первую очередь, и это использует SSL / HTTPS. - person Sanne; 01.03.2013
comment
@Rook: Я согласен с вами, что сеанс не должен обновляться каждый раз, так как это перебор, но как насчет регенерации только идентификатора с использованием session_regenerate_id (false), чтобы затруднить угадывание SID? - person Marco Sulla; 05.06.2013
comment
@Lucas Malor, это не может предотвратить нападение. Идентификатор должен быть достаточно большим, чтобы предотвратить грубую силу в нашей жизни. Если у вас есть нарушение owasp a9 или разделение HTTP-ответа / XSS / CSRF / Clickjakcing, то восстановление идентификатора не поможет. - person rook; 05.06.2013
comment
@Rook: Конечно, но если у меня есть сеанс, который истекает через долгое время, скажем, 2 недели, как это делает учетная запись Google, у злоумышленника есть 2 недели, чтобы попытаться получить мой SID. По вашему мнению, это маловероятно, если у меня есть SID, созданный сильной функцией хеширования, или кто-то может попробовать? - person Marco Sulla; 05.06.2013
comment
@Lucas Malor Ну, это зависит от того, что вы хешируете. Если вы используете диспетчер сеансов своей платформы (это то, что вам следует делать), то вы, вероятно, используете / dev / urandom, который является хешем sah1 пула энтропии. В этом случае вам, вероятно, понадобится несколько тысяч лет, чтобы угадать этот идентификатор сеанса. - person rook; 05.06.2013
comment
Еще один важный момент, касающийся счетчика и его изменения на клиенте: cookie должен быть HttpOnly ... поэтому очевидно, что клиентский JavaScript не имеет доступа к cookie и не может увеличивать счетчик. Было бы разумно пояснить, что в этом случае требуется 2 файла cookie. - person Alexis Wilke; 24.12.2013
comment
Разве 256 байт энтропии не излишни? 32 должно быть достаточно. В конце концов, 32 байта = 256 бит. - person Scott Arciszewski; 30.05.2015
comment
как насчет session.use_strict_mode? - person Akam; 02.06.2016
comment
Строгий режим @Akam - это бандаж для всего остального, указанного в сообщении. Если вы регенерируете при повышении привилегий, как указано в сообщении, строгий режим буквально ничего для вас не сделает. - person ircmaxell; 02.06.2016

Обе атаки сеанса преследуют одну и ту же цель: получить доступ к законному сеансу другого пользователя. Но векторы атаки разные:

  • При атаке фиксации сеанса злоумышленник уже имеет доступ на действительный сеанс и пытается заставить жертву использовать именно этот сеанс.

  • В ходе атаки перехвата сеанса злоумышленник пытается получить идентификатор сеанса жертвы, чтобы использовать его / ее сеанс.

В обеих атаках идентификатор сеанса - это конфиденциальные данные, на которые направлена ​​эта атака. Таким образом, это идентификатор сеанса, который необходимо защитить как для доступа для чтения (перехват сеанса), так и для доступа на запись (фиксация сеанса).

В этом случае также применяется общее правило защиты конфиденциальных данных с помощью HTTPS. Дополнительно необходимо сделать следующее:

Чтобы предотвратить атаки Session Fixation, убедитесь, что:

  • идентификатор сеанса принимается только из файла cookie (установите session.use_only_cookies на true) и сделайте его для HTTPS, только если возможно (установите session.cookie_secure на true); вы можете сделать и то, и другое с помощью session_set_cookie_params.

Чтобы предотвратить атаки перехвата сеанса, убедитесь, что:

Чтобы предотвратить обе сеансные атаки, убедитесь, что:

  • принимать только сеансы, инициированные вашим приложением. Вы можете сделать это, сняв отпечаток сеанса при запуске с конкретной информацией о клиенте. Вы можете использовать идентификатор User-Agent, но не использовать удаленный IP-адрес или любую другую информацию, которая может измениться между запросами.
  • для изменения идентификатора сеанса с помощью session_regenerate_id(true) после попытки аутентификации (true только в случае успеха) или изменения прав и уничтожения старая сессия. (Обязательно сохраните любые изменения $_SESSION с помощью session_write_close перед регенерацией идентификатора, если вы хотите сохранить сеанс, связанный со старым идентификатором; в противном случае эти изменения затронут только сеанс с новым идентификатором. )
  • чтобы использовать правильную реализацию истечения срока действия сеанса (см. Как завершить сеанс PHP через 30 минут?).
person Gumbo    schedule 22.02.2011
comment
Классный пост, особенно последний раздел. - person Mattis; 29.05.2011

Указанные вами токены - это одноразовые числа, которые используются один раз. Их не обязательно использовать только один раз, но чем дольше они используются, тем выше вероятность того, что одноразовый номер может быть захвачен и использован для перехвата сеанса.

Еще один недостаток одноразовых номеров состоит в том, что очень сложно построить систему, которая их использует и допускает несколько параллельных окон в одной и той же форме. например пользователь открывает два окна на форуме и начинает работу над двумя сообщениями:

window 'A' loads first and gets nonce 'P'
window 'B' loads second and gets nonce 'Q'

Если у вас нет возможности отслеживать несколько окон, вы сохранили только одно временное значение - одноразовый номер окна B / Q. Когда пользователь затем отправляет свое сообщение из окна A и передает одноразовый номер «P», эта система отклонит сообщение как P != Q.

person Marc B    schedule 22.02.2011
comment
Так при чем здесь фиксация сеанса? - person rook; 22.02.2011
comment
У него есть веская причина, особенно в области одновременного использования множества запросов AJAX. - person DanielG; 18.04.2012

Я не читал статью Шифлетта, но думаю, вы что-то неправильно поняли.

По умолчанию PHP передает токен сеанса в URL-адрес всякий раз, когда клиент не принимает файлы cookie. В противном случае в наиболее частом случае токен сеанса сохраняется как файл cookie.

Это означает, что если вы поместите токен сеанса в URL-адрес, PHP распознает его и попытается использовать впоследствии. Фиксация сеанса происходит, когда кто-то создает сеанс, а затем обманом заставляет другого пользователя использовать тот же сеанс, открыв URL-адрес, содержащий токен сеанса. Если пользователь каким-либо образом аутентифицируется, злоумышленник узнает токен сеанса аутентифицированного пользователя, у которого могут быть другие привилегии.

Я уверен, что Шифлетт объясняет, что обычно нужно повторно создавать новый токен каждый раз, когда права пользователя меняются.

person Andrea    schedule 22.02.2011
comment
Чтобы добавить к этому, пожалуйста, обязательно уничтожьте все ранее открытые сеансы, поскольку они все еще будут действительны с существующими разрешениями пользователя. - person corrodedmonkee; 22.02.2011

Да, вы можете предотвратить фиксацию сеанса, повторно создав идентификатор сеанса один раз при входе в систему. Таким образом, если злоумышленник не будет знать значение cookie только что аутентифицированного сеанса. Другой подход, который полностью решает проблему, - это установить session.use_only_cookies=True в вашей конфигурации времени выполнения. Злоумышленник не может установить значение cookie в контексте другого домена. Фиксация сеанса полагается на отправку значения cookie как GET или POST.

person rook    schedule 22.02.2011