- PVSM.RU - https://www.pvsm.ru -
На некоторой стадии развития веб-проекта возникает одна из следующих ситуаций:
Традиционно в таких случаях для хранения пользовательских сессий начинают использовать Redis, Memcached или какое-то другое внешнее хранилище. Как следствие возникает бремя эксплуатации базы данных, которая при этом не должна быть единой точкой отказа или бутылочным горлышком в системе.
Однако, есть альтернатива этому подходу. Возможно безопасно и надёжно хранить данные сессии в браузерной куке у самого пользователя, если заверить данные сессии криптографической подписью. Если вдобавок к этому данные ещё и зашифровать, то тогда содержимое сессии не будет доступно пользователю. Главное достоинство этого способа хранения в том, что он не требует централизованной базы данных для сессий со всеми вытекающими из этого плюсами в виде надёжности, скорости и масштабирования.
Эта идея не нова и реализована во множестве фрэймворков и библиотек для различных языков программирования. Вот пара примеров:
Стоит заметить, в Ruby on Rails делают большую ставку на производительность этого механизма в сравнении со всеми остальными методами хранения сессий и используют его по умолчанию.
Большинство имеющихся реализаций работают следующим образом: записывают в какую-то куку строку, содержащую время истечения сессии, данные сессии и HMAC-подпись [3] времени истечения и данных. При запросе клиента кука читается соответствующим обработчиком, затем проверяется подпись и сравнивается текущее время с временем истечения сессии. Если всё совпадает, обработчик возвращает данные сессии в приложение.
Однако, шифрование куки в распространённых реализациях этого механизма отсутствует.
В итоге, хранение сессий в куках имеет следующие достоинства:
Имеются и недостатки, куда же без них:
Когда я попытался отыскать что-то похожее для PHP, я с удивлением обнаружил, что не существует ни одной библиотеки, которая дотягивает до минимума требований:
Кроме этого считаю вовсе не лишним:
Реализации, которые я рассмотрел:
Репозиторий | Комментарий |
---|---|
github.com/Coercive/Cookie [5] | Фактически не библиотека для работы с сессиями вовсе. Ставит шифрованную куку, не подписывая её. |
github.com/stevencorona/SessionHandlerCookie [6] | Ближе всего к требованиям, но всё же имеет значительные недостатки:
|
github.com/mapkyca/Encrypted-Client-Side-Sessions [9] |
|
Также я смотрел реализацию хранения сессий в куках в фрэймворке Slim версии 2.x, но там нет ни подписи, ни шифрования. О чём авторы сразу и предупреждают.
Почему важна проверка подписи и шифрования вместо подписи недостаточно? Во-первых, есть заметная вероятность, что кука с мусором расшифруется в какую-то сессию, особенно запись сессии короткая. Во-вторых, строка с сессией подвергается десериализации, а на вход десериализатора нельзя подавать строки из недоверенных источников.
После всех поисков я решил реализовать такую библиотеку самостоятельно.
Packagist: packagist.org/packages/snawoot/php-storageless-sessions [12]
Github: github.com/Snawoot/php-storageless-sessions [13]
Установка из composer: composer require snawoot/php-storageless-sessions
Ключевые особенности:
Пара слов о выборе режима шифрования. При использовании блочных режимов шифрования (ECB, CBC) длина шифротекста незначительно возрастает. Это связано с тем, что длина исходного сообщения должна быть кратна размеру блока. Из-за обязательного паддинга прирост длины составляет от одного байта до размера блока шифра. То есть для AES — от 1 до 16 байт. При использовании потоковых режимов шифрования (OFB, CFB, CTR, ...) исходное сообщение не пропускается через блочный шифр, вместо этого блочный шифр используется для образования гамма-последовательности, и тогда длина шифротекста точно соответствует длине исходного сообщения, что лучше подходит для описываемой задачи.
Небольшой скрипт, иллюстрирующий работу с этим хэндлером:
<?php
require_once("vendor/autoload.php");
header('Content-Type: text/plain');
$secret = '********************';
$handler = new VladislavYarmakStoragelessSessionCryptoCookieSessionHandler($secret);
session_set_save_handler($handler, true);
session_start();
if ($_GET) {
foreach ($_GET as $key => $value)
$_SESSION[$key] = $value;
echo "Updated session:";
} else
echo "Current session data:n";
var_dump($_SESSION);
Пронаблюдать его работу, задавая разные значения сессии в строке запроса, можно по адресу: https://vm-0.com/sess.php [14].
Пример интеграции в Symfony:
framework:
session:
handler_id: session.handler.cookie
services:
session.handler.cookie:
class: VladislavYarmakStoragelessSessionCryptoCookieSessionHandler
public: true
arguments: ['reallylongsecretplease']
В качестве реального демо я подключил этот хэндлер сессий к первому пришедшему на ум веб-приложению, которое использует сессии. Им оказалось DokuWiki: wiki.vm-0.com [15]. На сайте работает регистрация и логин, а работу сессий можно наблюдать в куках.
Благодарю за внимание и надеюсь, что эта статья поможет развитию ваших проектов.
Автор: YourChief
Источник [16]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/php-2/251746
Ссылки в тексте:
[1] Python/Django: https://docs.djangoproject.com/en/1.10/topics/http/sessions/#using-cookie-based-sessions
[2] Ruby/Ruby on Rails: http://api.rubyonrails.org/classes/ActionDispatch/Session/CookieStore.html
[3] HMAC-подпись: https://ru.wikipedia.org/wiki/HMAC
[4] SessionHandlerInterface: http://php.net/manual/ru/class.sessionhandlerinterface.php
[5] github.com/Coercive/Cookie: https://github.com/Coercive/Cookie
[6] github.com/stevencorona/SessionHandlerCookie: https://github.com/stevencorona/SessionHandlerCookie
[7] атакам по времени: https://ru.wikipedia.org/wiki/%D0%90%D1%82%D0%B0%D0%BA%D0%B0_%D0%BF%D0%BE_%D0%B2%D1%80%D0%B5%D0%BC%D0%B5%D0%BD%D0%B8
[8] прямого сравнения хэша: https://github.com/stevencorona/SessionHandlerCookie/blob/master/src/SessionHandler/Storage/SecureCookie.php#L72
[9] github.com/mapkyca/Encrypted-Client-Side-Sessions: https://github.com/mapkyca/Encrypted-Client-Side-Sessions
[10] Отсутствие подписи: https://github.com/mapkyca/Encrypted-Client-Side-Sessions/blob/master/cookie.class.php#L57-L78
[11] Использование для шифрования статического IV: https://github.com/mapkyca/Encrypted-Client-Side-Sessions/blob/master/cookie.class.php#L18
[12] packagist.org/packages/snawoot/php-storageless-sessions: https://packagist.org/packages/snawoot/php-storageless-sessions
[13] github.com/Snawoot/php-storageless-sessions: https://github.com/Snawoot/php-storageless-sessions
[14] https://vm-0.com/sess.php: https://vm-0.com/sess.php
[15] wiki.vm-0.com: https://wiki.vm-0.com/
[16] Источник: https://habrahabr.ru/post/325452/
Нажмите здесь для печати.