Сессии PHP + Useragent с солью - PullRequest
       17

Сессии PHP + Useragent с солью

4 голосов
/ 05 марта 2009

Последние несколько дней я продолжаю думать об этом, но я прочитал несколько статей о том, как сделать ваши PHP-сессии более безопасными. Почти во всех этих статьях говорится, что вам нужно сохранить идентификатор пользователя в сеансе С ​​дополнительной солью. Примерно так:

$fingerprint = md5('SECRET-SALT'.$_SERVER['HTTP_USER_AGENT']);

Из-за соли злоумышленнику будет сложнее захватить или что-то еще во время сеанса. Но ПОЧЕМУ добавляйте соль каждый раз, когда вы проверяете ее следующим образом:

md5('SECRET-SALT'.$_SERVER['HTTP_USER_AGENT']) == $_SESSION [ 'fingerprint' ]

Так ПОЧЕМУ соль может сделать ее более безопасной, поскольку злоумышленнику по-прежнему нужны только пользовательский агент (который является относительно небольшим набором различных пользовательских агентов) и сессионный идентификатор?

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

Спасибо!

Ответы [ 8 ]

7 голосов
/ 06 марта 2009

Причина, по которой предлагается добавить соль, проста. Обычно, когда вы создаете этот «отпечаток» - если вы используете только один элемент данных, который имеет ограниченный набор данных, тогда внешнему хакеру будет проще сгенерировать его и захватить сеанс.

В приведенном выше примере, да, если у злоумышленника есть и «отпечаток пальца», и пользовательский агент, он сможет перехватить сеанс.

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

Я бы предложил добавить еще кое-что, например, в vBulletin (проект, над которым я работал), хэш идентификатора сеанса (который в основном совпадает с отпечатком) генерируется с помощью следующего кода.

define('SESSION_IDHASH', md5($_SERVER['HTTP_USER_AGENT'] . $this->fetch_substr_ip($registry->alt_ip))); // this should *never* change during a session

Кроме того, хэш сеанса генерируется с использованием

md5(uniqid(microtime(), true));

Они оба проверяются при попытке идентифицировать сеанс

Итак, чтобы захватить сессию, человеку необходимо знать следующее

  • Время (точно) на сервере, когда был создан сеанс
  • Строка агента браузера пользователя
  • IP-адрес пользователя

Они также должны были бы подделать IP-адрес (или, по крайней мере, первые 2/3 октета), чтобы сделать это.

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

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

Например, в чем-то, что я сейчас пишу на python, я генерирую хэш для использования с защитой XSRF. Вот что я использую.

    self.key = sha1(
        self.user.username +
        self.user.password +
        settings.SECRET_KEY +
        strftime("%a, %d %b %Y %H:%M:%S +0000", gmtime())
    ).hexdigest()

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

3 голосов
/ 22 августа 2009

Если вы находитесь на своем собственном сервере, шифровать переменные сеанса бессмысленно, потому что они не выходят с сервера. См. Linead ответ на Что мне нужно хранить в сеансе php, когда пользователь вошел в систему? для получения дополнительной информации. Если вы находитесь на общем сервере, вам может потребоваться зашифровать все переменные сеанса, кроме идентификатора сеанса, поскольку они хранятся во временных файлах, которые можно прочитать на том же веб-сервере, который используют все ваши соседи.

В любом случае, если вы действительно беспокоитесь о безопасности, вам лучше работать с собственным (виртуальным или нет) сервером, поэтому опасность будет исходить только за пределами вашего сервера.

Некоторые примеры риска для ваших сессий:

  • Ваш сервер отправляет идентификатор сеанса в URL-адресе, а ваш пользователь переходит по ссылке на badguys.com. В переменных сервера они получат реферер (полный URL-адрес, включая идентификатор сеанса), браузер и IP-адрес вашего пользователя. , Если вы не проверяете IP-адреса или ваш пользователь использует открытый прокси-сервер, ему нужно только установить ту же версию браузера, вставить URL-адрес, и все готово.
  • Пользователь заходит на общедоступный ПК, входит в систему и выходит, не закрывая сеанс (в конце концов, он человек). Следующий парень в строке открывает браузер, проверяет историю и находит открытую сессию. Тьфу.

Итак, некоторые меры, которые вы можете предпринять, по моим обычным предпочтениям:

  1. Не отправлять идентификатор сеанса в URL; включить session.use_only_cookies в PHP. Минусы: пользователю необходимо включить куки.
    • В случае опасных действий (сменить пароль, сделать заказ ...), снова спросить пользователя пароль. Вы также можете делать это периодически. Минусы: раздражает.
    • Таймаут сессий быстро. Минусы: на большинстве сайтов это заставит пользователей часто заходить в систему, раздражая их.
    • Использовать SSL (единственный способ избежать атак «человек посередине»). Минусы: медленно. Глупые сообщения браузера. Требуется SSL на сервере.
    • Проверьте IP. Минусы: неэффективно для посетителей, использующих публичный прокси. Раздражает динамические IP-адреса.
    • Проверьте пользовательский агент (браузер). Минусы: практически бесполезны, UA легко получить и имитировать просто.

(я считаю само собой разумеющимся, у вас есть PHP, настроенный для максимальной безопасности).

Еще несколько крайностей:

  • Поддерживать постоянную связь между сервером и браузером, например, используя Java-апплет. Нет связи, нет сессии. Минусы: пользователю нужны Java, ActiveX или все, что вы используете. Сессия закрывается браузером (это может быть хорошо). Не работает на очень медленных соединениях. Более высокая нагрузка на сервер. Вам нужно открыть порты, иметь специальный сервер для апплета.
  • То же самое, но с использованием асинхронных запросов (например, AJAX) для очень частого обновления сеанса и очень короткого времени ожидания. Или обновить скрытый IFRAME. Минусы: пользователю нужен JavaScript. Не работает на очень медленных соединениях. Более высокая нагрузка на сервер.
  • То же самое, но перезагрузка всей страницы. Минусы: пользователю нужен JavaScript. Автоматическая перезагрузка при чтении страницы очень раздражает.

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

2 голосов
/ 06 марта 2009

Если я правильно понимаю, вы хотите предотвратить захват сеанса удаленным злоумышленником, который угадывает идентификаторы сеанса?

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

Если вы сохраняете строку пользовательского агента для «блокировки» сеанса с текущим пользовательским агентом, тогда нет смысла ее хешировать - сравнение строк по полной строке пользовательского агента выполняется быстрее (затем хеширование, а затем сравнение) и не намного дороже с точки зрения хранения.

Я не верю, что хранение пользовательского агента обеспечивает достаточную дифференциацию - лучше было бы сгенерировать больший идентификатор (с большим количеством битов) во время начала сеанса (возможно, sha1 текущая отметка времени + имя пользователя + пользовательский агент + что-то ), затем сохраните его в файле cookie, а также в сеансе и сопоставьте его с каждым дополнительным запросом. Это не сильно меняет вектор атаки (вам нужно угадать какое-то число), но его легко значительно увеличить количество битов, которые необходимо угадать для успешной атаки, за счет огромного увеличения сложности атаки.

1 голос
/ 20 октября 2009

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

0 голосов
/ 06 марта 2009

Хорошо, например, я использую следующий вымышленный код:

<?php

// The sessionid cookie is now a certain hash
if ( array_key_exists ( $_COOKIE [ 'sessionid' ] ) )
{
    // Get the session from database
    $db_sessid = $pdo -> getStuff ( 'session_database', $_COOKIE [ 'sessionid' ] );

    if ( $db_sessid !== null && $db_sessid [ 'fingerprint' ] == sha1 ( 'SOMESALT' . $_SERVER [ 'HTTP_USER_AGENT' ] ) )
    {
        set_cookie ( ... ); // New sessionid and write also to DB

        // User is now logged in, execute some user stuff
    }
    else
    {
        // Session doesn't exist, or the fingerprint does not match
    }
}

Теперь злоумышленнику по-прежнему нужен только идентификатор сеанса, который находится в файле cookie (отправляемом по заголовкам HTTP) и в идентификаторе пользователя. Так какой же смысл в дополнительной соли?

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

Спасибо, пока (-:

0 голосов
/ 06 марта 2009

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

Использование удаленного адреса пользователя также не без проблем. Многие люди используют один и тот же компьютер из разных мест. Мобильные устройства, ноутбуки, используемые дома и на работе, ноутбуки, используемые в точках доступа Wi-Fi и так далее. ИМХО, это плохая идея использовать IP-адрес таким образом, чтобы новый IP-адрес требовал логин , если вы не имеете дело с очень конфиденциальной информацией , такой как онлайн-банкинг. Это тот случай?

Что вас беспокоит? Внешняя атака? Или в ситуации с общим хостом, когда кто-то может прочитать информацию о вашем сеансе?

Если это последнее, решение простое: просто не сохраняйте ничего чувствительного в сеансе. Все важные данные должны храниться в базе данных.

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

Что касается того, что сделает его более безопасным, вы сказали сами: количество строк пользовательских агентов ограничено (менее ста, вероятно, охватит 99,99% пользователей). Соль просто увеличивает количество возможностей. При этом, если вы используете одну и ту же соль для всех сессий, то это только вопрос времени, когда она будет найдена с грубой силой.

0 голосов
/ 05 марта 2009

Поскольку отпечаток хранится на стороне сервера, вам не нужно использовать соленый хеш. «Нормального» хеша достаточно, чтобы уменьшить данные.

0 голосов
/ 05 марта 2009

Я делаю это также для частичной защиты от атак на олицетворение сеанса . Вам также необходимо указать IP-адрес.

Имейте в виду, что при автоматическом обновлении браузера клиента изменяется пользовательский агент, и вы будете думать, что его сеанс был угнан;)

...