Защита идентификатора сессий в PHP

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

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

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

В данной статье я постараюсь изложить все известные мне способы защиты идентификатора сессии в PHP.

Итак, поехали.


Использование cookie


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

www.example.org/index.php?PHPSESSID=n2cnj59d7s3p30fjs0jfn28nf

Здесь ID передается в открытом виде, в отличие от сессии через cookie, когда информация скрыта в HTTP-заголовке. Самым простым способом защиты от этого будет запрет передачи идентификатора сессии через адресную строку. Сделать это можно, прописав следующее в конфигурационном файле Apache-сервера .htaccess:

php_flag session.use_only_cookies on

Использование шифрования


Если на вашем сайте должна обрабатываться конфиденциальная информация, такая как номера кредитных карт (привет от Sony), следует использовать SSL3.0 или TSL1.0 шифрование. Для этого при установке cookie следует указывать true для параметра secure.

Если вы храните пароль сессии в переменной $_SESSION (все-таки лучше использовать sql), то не стоит хранить его в открытом виде.

if ($_SESSION['password'] == $userpass) {
    // код	
}


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

if ($_SESSION['md5password'] == md5($userpass)) {
    // код	
}


Проверка браузера


Чтобы отсечь возможность использования сессии с другого браузера (компьютера), следует ввести проверку поля HTTP-заголовка user-agent:

session_start();
if (isset($_SESSION['HTTP_USER_AGENT']))
{
    if ($_SESSION['HTTP_USER_AGENT'] != md5($_SERVER['HTTP_USER_AGENT']))
    {
        // код
    }
}
else
{
    $_SESSION['HTTP_USER_AGENT'] = md5($_SERVER['HTTP_USER_AGENT']);
}


Срок действия сессии


Ограничьте время жизни сессии, а также время действия cookie. По умолчанию срок действия сессии 1440 секунд. Изменить это значение можно через php.ini и .htaccess. Пример для .htaccess:

# Время жизни сессии в секундах
php_value session.gc_maxlifetime 3600
# Время жизни куки в секундах
php_value session.cookie_lifetime 3600


Привязка по IP-адресу


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

include ("ip_list.php");

//$ip_white_list = array ( 
	'admin1' => '111.222.333.444',
	'admin2' => '555.666.777.888');

if(!empty(array_search($_SERVER['REMOTE_ADDR'],$ip_white_list))) 
{
    header("Location: admin.php");
}
else 
{
    echo 'ACCESS DENY!';
}


либо по IP-адресу для каждого запроса (только для статичных IP):

if(isset($_SESSION['ip']) and $_SESSION['ip'] == $_SERVER['REMOTE_ADDR']) 
{
    header("Location: admin.php");
}
else 
{
    session_unset();
    $_SESSION['ip'] = $_SERVER['REMOTE_ADDR'];
}


Следует осознавать, что полностью избежать взлома невозможно. Можно только максимально усложнить этот взлом любыми известными способами. Однако следует также не забывать о своих легальных пользователях, чтобы не осложнить им жизнь такой защитой.
Поделиться публикацией

Похожие публикации

Комментарии 23

    0
    Можно было бы добавить про session_regenerate_id().
      +3
      Не стоит при сохранении пароля в $_SESSION делать хешировани, но лишь потому, что паролю и даже его хешу в сессиях не место вовсе, не могу даже придумать случая, когда это нужно, ведь в $_SESSION можно хранить идентификатор пользователя из базы.
      Еще бы добавил, что нужно стараться логин/пароль передавать по https.
      А чтобы с одного IP вас не начали сканировать или досить или роботом кравлить ваш контент, то имеет смысл ограничить количество идентификаторов сессий, которые вы выдаете на один IP. Обычно роботы сессии не поддерживают, поэтому, если 100 раз в минуту обратились и ни разу не предъявили уже сгенерированный идентификатор сессии, то тут и подумаешь, генерить ли 101-ый.
        0
        А не подскажите, как ограничить кол-во сессий на один IP?
          0
          Нужно где-то сделать счетчик, например в базе данных или memcached. Структура данных такая: таблица сессий (каждому выданному идентификатору сессии сопоставляется время выдачи и IP), можно ограничиться одной таблицей, а можно еще таблицу с IP адресами (счетчик выданных сессий и флаг, например, для организации блеклистов, можно еще время последнего обращения). Вопрос десяти-двадцати строчек кода, на самом деле.
        +6
        Во-1х, md5 уже небезопасный. Из него можно получить как пароль (в случае простых паролей), так и просто сгенерировать строку, которая подойдёт под нужный md5 хеш.

        Во-2х, безопасность хранения пароля в сессии не стоит просто потому, что ему там неоткуда взяться. Пароли сверяются с базой пользователей, в которой уже как раз пароли не должны храниться открытым текстом — именно поэтому это вообще не к месту.

        В-3их, хранение любых данных в сессии относительно безопасно, если сессии хранятся только памяти и только на своём сервере, во всех остальных случаях следует учесть, что существуют способы «угадать» или пробраться к файлу сессии (особенно на shared хостингах неудачных) из другого проекта, и потому данным сессии доверять вообще не особо-то можно

        В-4ых, Код вот этот:
        
        if(!empty(array_search($_SERVER['REMOTE_ADDR'],$ip_white_list))) 
        {
            header("Location: admin.php");
        }
        else 
        {
            echo 'ACCESS DENY!';
        }
        

        решает какую-то несусветную фигню, чесслово. То есть если IP нормальный, то мы делаем redirect на другой файл.А что, тот файл не должен проверять? :) Если уж ограничиваем по IP, то так:
        
        // in admin.php 
        if(!in_array($_SERVER['REMOTE_ADDR'],$ip_white_list))
        {
            die('ACCESS DENY!');
        }
        
          0
          Вместо md5 можно использовать sha1, это не принципиально в этом примере. А в остальных случаях соглашусь с Вами.
            0
            sha256, и с солью. иначе rainbow tables по-прежнему рулят.
        • НЛО прилетело и опубликовало эту надпись здесь
            0
            Разница между заголовками и URL огромная:
            1) При передаче в URL простой копипаст ссылки отдаёт SID
            2) При передаче в URL в логах вебсервера, прокси, клиента, и различных трекерах текущего сайта SID светится
            Поэтому да, передача только в куках лучше, чем передача в URL.

            Не нужно взламывать сервер. Достаточно, например, иметь сайт на том же shared тазике, когда файлы сессий разных сайтов не изолированы друг от друга.
            Зачастую файлы сайтов изолированы, а вот сессии нет.
            0
            Я бы сказал, что md5 там только что бы сократить длинную строку до 32 символов. Безопасность тут ни причём. Если злоумышленник уведёт SID из куков, то подделать USER-AGENT сможет без проблем. Тем более, что USER-AGENT — это не пароль и существует ограниченное кол-во различных вариантов. А если у жертвы стандартный браузер последней версии, то методом перебора можно угадать с 10 попыток.
              0
              Это в user-agent md5() для сокращения. А вот в пароле для недопущения утечки пароля из файла сессии.
                0
                Ну тогда зачем пароль хранить в том месте откуда его могут спереть? То что он зашифрован — не имеет значения. Если его могут украсть, то это уже плохо.
                Неужели так сложно написать:
                unset($user['password']);
                прежде чем добавлять $user в $_SESSION? У меня unset вообще в модели выполняется, по этом никто кроме базы пароля никогда не знает.
                  0
                  Об этом я и говорил. Мне другое не нравится — использование md5($pass) уже давным-давно неэффективно (где-то с 2002 года), а его по-прежнему всюду пихают. Вот прямо как есть, даже без соли.
                  Люди начитываются и начинают так делать. За что потом платятся.
                0
                А если md5($ID + $USERAGENT + $SALT + [$IP]) — то подбор становится уже значительно сложнее.
                  +1
                  А это уже стандартный алгоритм валидации сессий. Называется он «отпечатки пальцев» и описан в нормальных статьях про сессии. См. мой коммент ниже.
              +2
              Похоже, вы не знаете об этом: HttpOnly
              (на wiki меньше букав)
                0
                Если вы храните пароль сессии в переменной $_SESSION (все-таки лучше использовать sql)

                угу, т.е. если вместо $_SESSION я буду использовать $session станет лучше?
                Статья и так детская, так и пишите что есть различные варианты хранения сессий, часть реализована внутри РНР а остальные можно сделать самому — документация
                так же как писали выше — пароль не нужен и к этому же — несолёный sha1 не лучше солёного md5
                впрочем с бредом про «доверять сессии не особо можно» я не согласен — если можно пробиваться на чужие виртуалки это баг хостера, а не недочёт программиста не учевшего такой вариант
                Ограничьте время жизни сессии

                и спрашивается нафига? на многих сайтах есть кнопка «запоминать меня» — как это в таком случае сделать?
                храни таймаут в сессии, если надо, и только если действительно надо дропать все просроченные сессии — можно пользоваться этой опцией
                  +2
                  Статья для совсем новичков.
                  1. Фильтровать всех юзеров по IP — а причём тут сессии?
                  2. IP надо записывать в сессию и при повторном обращении проверять его так же как вы проверяете USER-AGENT. Это даст дополнительную степень надёжности если кто-то уведёт SID но будет находится в другой (под)сети.
                  3. Сверять USER-AGENT не очень надёжно, ведь если злоумышленник уведёт SID из куков, то user-agent подделает без проблем. Его даже красть не обязательно — можно перебором.
                  4. Конечно сравнение user-agent не будет лишним но так же стоит добавить, что иногда эта проверка косячит и мешает нормальной работе. Я, например, сталкивался с кривыми браузерами, которые слали рандомный user-agent в каждом запросе. Ещё флеш предыдущей версии может тупить при аплоаде файлов и посылать не тот заголовок, которые был до этого. В итоге на нескольких проектах мне пришлось выключать эту проверку.
                  5. Ну и конечно ничего не рассказано про «отпечатки пальцев».

                  В итоге, эта статья в подмётки не годится старой статье на пхп-клубе: phpclub.ru/detail/article/sessions
                    +1
                    Ну так есть раздел «Веб-разработка для начинающих», нужно ее туда и переместить: habrahabr.ru/blogs/webdev_for_dummies/
                      0
                      О, автор перенес в раздел «Веб-разработка для начинающих», надеюсь, он еще допишет, проведет более глубокие исследования, учтет все высказанные мнения и подсказки и статья улучшится.

                  Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                  Самое читаемое