- backend перестаёт помещаться на одном сервере и требуется хранилище сессий, общее для всех backend-серверов
- по различным причинам перестаёт устраивать скорость работы встроенных файловых сессий
Традиционно в таких случаях для хранения пользовательских сессий начинают использовать Redis, Memcached или какое-то другое внешнее хранилище. Как следствие возникает бремя эксплуатации базы данных, которая при этом не должна быть единой точкой отказа или бутылочным горлышком в системе.
Однако, есть альтернатива этому подходу. Возможно безопасно и надёжно хранить данные сессии в браузерной куке у самого пользователя, если заверить данные сессии криптографической подписью. Если вдобавок к этому данные ещё и зашифровать, то тогда содержимое сессии не будет доступно пользователю. Главное достоинство этого способа хранения в том, что он не требует централизованной базы данных для сессий со всеми вытекающими из этого плюсами в виде надёжности, скорости и масштабирования.
Описание механизма
Эта идея не нова и реализована во множестве фрэймворков и библиотек для различных языков программирования. Вот пара примеров:
Стоит заметить, в Ruby on Rails делают большую ставку на производительность этого механизма в сравнении со всеми остальными методами хранения сессий и используют его по умолчанию.
Большинство имеющихся реализаций работают следующим образом: записывают в какую-то куку строку, содержащую время истечения сессии, данные сессии и HMAC-подпись времени истечения и данных. При запросе клиента кука читается соответствующим обработчиком, затем проверяется подпись и сравнивается текущее время с временем истечения сессии. Если всё совпадает, обработчик возвращает данные сессии в приложение.
Однако, шифрование куки в распространённых реализациях этого механизма отсутствует.
Сравнение с классическим подходом
В итоге, хранение сессий в куках имеет следующие достоинства:
- Возрастает производительность веб-приложения, так как небольшая криптографическая операция дешевле сеанса сетевого обмена или доступа к диску для извлечения данных сессии.
- Возрастает надёжность веб-приложения, так как оно не зависит от внешнего KV-хранилища. Даже если хранилище сессий обеспечено средствами отказоустойчивости, это не наделяет его абсолютной стабильностью: переключение требует времени, а часть проблем (такие как ухудшение сетевой связности между регионами) и вовсе неискоренимы. Зачастую же сессии и вовсе хранятся на единственном сервере, являющимся единой точкой отказа всего веб-приложения.
- Экономия ресурсов. Не нужно больше хранить сессии, а значит от этого выиграют и владельцы маленьких сайтов, у которых сократится дисковая активность, и освободят несколько серверов владельцы крупных веб-проектов.
Имеются и недостатки, куда же без них:
- Возрастает объём данных, передаваемый клиентом
- Имеется ограничение на размер данных в сессии, связанное с ограничениями на размер кук. Обычно это чуть меньше 4 КБ кодированных данных.
- Клиент может откатить состояние сессии на любое выданное и подписанное ранее значение, криптоподпись которого ещё действительна в текущий момент времени.
Реализации для PHP
Когда я попытался отыскать что-то похожее для PHP, я с удивлением обнаружил, что не существует ни одной библиотеки, которая дотягивает до минимума требований:
- Безопасность: отсутствие ошибок при использовании криптографии
- Актуальная кодовая база: поддержка современных версий PHP, отсутствие deprecated-расширений в зависимостях (таких как mcrypt)
- Наличие тестов: сессии — это один из фундаментальных механизмов, и в основе реального приложения нельзя использовать незрелый код
Кроме этого считаю вовсе не лишним:
- Возможность шифрования: открытое хранилище сессии на клиенте, читаемое клиентом, не всем подходит.
- Максимально компактное представление данных — ради минимизации оверхеда и запаса ёмкости сессии
- Встраиваемость через SessionHandlerInterface
Реализации, которые я рассмотрел:
Репозиторий | Комментарий |
---|---|
github.com/Coercive/Cookie | Фактически не библиотека для работы с сессиями вовсе. Ставит шифрованную куку, не подписывая её. |
github.com/stevencorona/SessionHandlerCookie | Ближе всего к требованиям, но всё же имеет значительные недостатки:
|
github.com/mapkyca/Encrypted-Client-Side-Sessions |
Также я смотрел реализацию хранения сессий в куках в фрэймворке Slim версии 2.x, но там нет ни подписи, ни шифрования. О чём авторы сразу и предупреждают.
Почему важна проверка подписи и шифрования вместо подписи недостаточно? Во-первых, есть заметная вероятность, что кука с мусором расшифруется в какую-то сессию, особенно запись сессии короткая. Во-вторых, строка с сессией подвергается десериализации, а на вход десериализатора нельзя подавать строки из недоверенных источников.
После всех поисков я решил реализовать такую библиотеку самостоятельно.
Собственная реализация
Packagist: packagist.org/packages/snawoot/php-storageless-sessions
Github: github.com/Snawoot/php-storageless-sessions
Установка из composer:
composer require snawoot/php-storageless-sessions
Ключевые особенности:
- Обязательное шифрование. Алгоритм и режим — любой симметричный шифр на выбор, доступный в OpenSSL. По умолчанию: AES-256-CTR.
- HMAC-подпись куки любым хэш-алгоритмом на выбор из ассортимента криптографического расширения Hash. Он же используется для генерации производных ключей шифрования. По умолчанию: SHA-256.
- Реализованы контрмеры против атак по времени
- Помимо основного набора данных и времени истечения, подписью охвачен и ID сессии, что оставляет простор для связывания данных сессии с внешними данными.
- Реализация представлена в виде класса, совместимого с SessionHandlerInterface, а значит её можно прозрачно использовать практически с любыми PHP-приложениями.
- Минимальный оверхед хранения, привносимый шифрованием и подписью.
Пара слов о выборе режима шифрования. При использовании блочных режимов шифрования (ECB, CBC) длина шифротекста незначительно возрастает. Это связано с тем, что длина исходного сообщения должна быть кратна размеру блока. Из-за обязательного паддинга прирост длины составляет от одного байта до размера блока шифра. То есть для AES — от 1 до 16 байт. При использовании потоковых режимов шифрования (OFB, CFB, CTR, ...) исходное сообщение не пропускается через блочный шифр, вместо этого блочный шифр используется для образования гамма-последовательности, и тогда длина шифротекста точно соответствует длине исходного сообщения, что лучше подходит для описываемой задачи.
Примеры использования
Небольшой скрипт, иллюстрирующий работу с этим хэндлером:
<?php
require_once("vendor/autoload.php");
header('Content-Type: text/plain');
$secret = '********************';
$handler = new VladislavYarmak\StoragelessSession\CryptoCookieSessionHandler($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.
Пример интеграции в Symfony:
framework:
session:
handler_id: session.handler.cookie
services:
session.handler.cookie:
class: VladislavYarmak\StoragelessSession\CryptoCookieSessionHandler
public: true
arguments: ['reallylongsecretplease']
В качестве реального демо я подключил этот хэндлер сессий к первому пришедшему на ум веб-приложению, которое использует сессии. Им оказалось DokuWiki: wiki.vm-0.com. На сайте работает регистрация и логин, а работу сессий можно наблюдать в куках.
Благодарю за внимание и надеюсь, что эта статья поможет развитию ваших проектов.
Комментарии (106)
basili4
04.04.2017 13:20-8Мне тут кажется имеется множество уязвимостей.
Например злоумышленник (З) может сам зарегистрироваться на сайте и будет примерно представлять содержимое шифрованной куки предположительно он может подобрать ключ к своей куки. Далее имея ключ и текст он может вычислить ключ для подписи.
Бороться этим можно только достаточно часто менять ключи. Что приведет к коллизиям у клиента кука подписана одним ключом, а на сервере он уже сменен. значит надо постоянно слать новые куки, что прям будут напрягать интернет. + Мы не уходим от узкого горлышка нам нужно хранить ключи для каждого пользователя.
Ну как то так.mayorovp
04.04.2017 13:29+5На момент написания этого комментария алгоритм AES достаточно защищен от такой атаки.
maxru
04.04.2017 13:22+1Я понимаю, конечно, что много всего держать в сессии плохо, но как работать с ограничением 4КБ на 1 куку?
Нет ли смысла в данном случае предусмотреть в том числе сохранение данных в SessionStorage?redfs
04.04.2017 16:18+1Нет ли смысла в данном случае предусмотреть в том числе сохранение данных в SessionStorage?
Нет, автор статьи сделал серверное решение, оно абсолютно прозрачно для клиента. Использование интерфейса Storage предполагает какую-то дополнительную логику на стороне клиента, а это из другой оперы.
nazarpc
04.04.2017 13:26+7То, что вы придумали обычно реализуют через JWT, для чего есть множество библиотек, в том числе на PHP.
А ещё есть статьи где популярно объясняется почему реализация сессий на клиенте это плохая идея в общем и с использованием JWT в частности:
https://paragonie.com/blog/2017/03/jwt-json-web-tokens-is-bad-standard-that-everyone-should-avoid
http://cryto.net/~joepie91/blog/2016/06/13/stop-using-jwt-for-sessions/
Ваша реализация, судя по тому что я прочел по диагонали, идеологически похожа, так что вторая статья (и её follow-up) весьма применима и для вашего случая.
mayorovp
04.04.2017 13:33Отмечу, что перечисленные недостатки относятся исключительно к использованию JWT в качестве хранилища сессионных данных.
nazarpc
04.04.2017 13:39+1Вы правда читали статью по второй ссылке?
Не зависимо от того, используется JWT или другой способ шифрованного хранения сессионных данных, все претензии к подходу из статьи остаются в силе. А первая ссылка на случай если кто-то решит использовать JWT как таковой в принципе.
mayorovp
04.04.2017 13:42+3Да, читал. В обоих статьях большая часть — бред и паника на пустом месте.
nazarpc
04.04.2017 13:43+1Тогда будьте добры, уточните что во второй статье относится исключительно к JWT и не релевантно данной статье.
mayorovp
04.04.2017 13:44Данной статье оно как раз очень даже релевантно. Оно не релевантно для использования JWT в тех задачах, для которых JWT создавался.
OlegMax
04.04.2017 15:24Читаем вторую статью. «The drawbacks:
- They take up more space
- You cannot invalidate individual JWT tokens
- Data goes stale
- Implementations are less battle-tested or non-existent»
Каждый пункт как-то отражен в данной статье, автор про это задумывался. Так что корректно было бы обсуждать каждый момент по существу, а не просто кидать ссылку.nazarpc
04.04.2017 15:41Ссылку я кинул для того, чтобы не повторять то, что кто-то уже структурировал в виде статьи. Задумываться над этим мало, нужно иметь решение к вопросам. Но исходя из их сущности решения скорее всего быть не может в общем случае. Если считаете иначе — давайте обсуждать.
Опять таки, чтобы не повторяться, рекомендую прочесть саркастический ответ на часто встречаемые контр-аргументы (он упомянут в шапке той статьи, если пропустили): http://cryto.net/~joepie91/blog/2016/06/19/stop-using-jwt-for-sessions-part-2-why-your-solution-doesnt-work/
OlegMax
04.04.2017 15:54+4В статьях, на которые вы ссылались, какие-то люди пытаются попиариться на относительно модном JWT. А реальность такова, что есть pros и cons у JWT-like и server-side сессий. У обоих есть фунадаментальные достоинства и недостатки и есть дефекты реализаций/спецификаций.
Надо стремиться использовать наиболее подходящее решение в каждом случае и исправлять дефекты, но заявлять «JWT sucks», как авторы тех статей — неправильно.nazarpc
04.04.2017 17:40Голословно — не правильно. А если аргументировано, указывая на фундаментальные ошибки и заблуждения, то вполне себе можно и нужно.
zerkms
04.04.2017 14:12+1JWT или другой способ шифрованного хранения сессионных данных
В JWT, кстати, данные хранятся в открытом виде.nazarpc
04.04.2017 14:15+1Не обязательно
zerkms
04.04.2017 14:19Обязательно, согласно спецификации https://tools.ietf.org/html/rfc7519
nazarpc
04.04.2017 14:23+1Вы Abstract читали (первый параграф)?:
JSON Web Token (JWT) is a compact, URL-safe means of representing
claims to be transferred between two parties. The claims in a JWT
are encoded as a JSON object that is used as the payload of a JSON
Web Signature (JWS) structure or as the plaintext of a JSON Web
Encryption (JWE) structure, enabling the claims to be digitally
signed or integrity protected with a Message Authentication Code
(MAC) and/or encrypted.Весьма однозначно сказано что содержимое вполне себе может быть зашифрованным.
zerkms
04.04.2017 14:25Так, как на хабре минусовать самого себя? :-D
Я всегда думал, что JWE это самостоятельный тип токена, отличный от JWT, пардон.
mayorovp
04.04.2017 14:28The contents of the JOSE Header describe the cryptographic operations applied to the JWT Claims Set. If the JOSE Header is for a JWS, the JWT is represented as a JWS and the claims are digitally signed or MACed, with the JWT Claims Set being the JWS Payload. If the JOSE Header is for a JWE, the JWT is represented as a JWE and the claims are encrypted, with the JWT Claims Set being the plaintext encrypted by the JWE. A JWT may be enclosed in another JWE or JWS structure to create a Nested JWT, enabling nested signing and encryption to be performed.
— https://tools.ietf.org/html/rfc7519#page-6
OlegMax
04.04.2017 15:28+2Первая статья — типа «Давайте не будем использовать HTTP — вон в нём сколько дыр уже нашли». Какой может быть ответ? Нет, спасибо, мы дырки-то позатыкаем, но использовать не бросим.
lair
04.04.2017 13:27+6Возможно безопасно и надёжно хранить данные сессии в браузерной куке у самого пользователя, если заверить данные сессии криптографической подписью.
… только до тех пор, пока "данные сессии" — маленькие. В противном случае вы получаете гигантский оверхед на каждом запросе.
VolCh
04.04.2017 14:00Как решается вопрос принудительной инвалидации сессии?
А вообще, по-моему, данный подход не является сессией в обычном смысле слова в веб-разработке. Сессия — хранение каких-то данных на стороне сервера такое, что результаты запросов клиента зависят от истории предыдущих запросов. Тут же каждый запрос получается самодостаточным, что-то вроде WebForms.
mayorovp
04.04.2017 14:16А WebForms-то тут при чем?..
VolCh
04.04.2017 15:13Может запамятовал, но вроде они отправляли с фронта на сервер всё состояние сессии, ну и получали его от сервера.
mayorovp
04.04.2017 15:20-1Они отправляли состояние страницы, а не сессии. Если у пользователя открыты сразу две вкладки — эти понятия принципиально различаются.
Кроме того, ViewState не сохраняется при переходах между страницами.
VolCh
04.04.2017 15:22Может. Я особо не разбирался, как фротендер с ними только немного работал больше 10 лет назад.
YourChief
05.04.2017 13:06Добрый день!
Как и указано в недостатках,
Клиент может откатить состояние сессии на любое выданное и подписанное ранее значение, криптоподпись которого ещё действительна в текущий момент времени.
Однако здесь важно заметить: если, к примеру, стоит практическая задача разлогинить юзера, то инвалидация одной сессии сама по себе ничего не даёт. Нужно прервать все сессии. И здесь у классического подхода к хранению сессий тоже возникают проблемы, так как в таком случае нужно найти и удалить все сессии, которые принадлежат пользователю. То есть это значит нужно хранить и поддерживать соответствие пользователя списку сессий. Дальше — больше: если используются sticky-сессии, то тогда один сервер должен пошариться по множеству других, чтобы разлогинить юзера через удаление сессий.
Удаление сессий в любом случае непрактичный подход. Вместо этого в сессию записывают факт логина вместе со временем логина. А в профиле пользователя держат время последнего логаута. Если время логина сессии раньше времени последнего логаута — считать её неактивной. При таком подходе для логаута нужно всего лишь обновить одно значение в записи о пользователе.lair
05.04.2017 13:35-2А в профиле пользователя держат время последнего логаута.
И как, бишь, вы это время между разными серверами будете синхронизировать в случае sticky sessions?
(и как вы будете решать задачу "разлогинь меня в этой сессии, не трогая остальные"?)
YourChief
05.04.2017 14:11И как, бишь, вы это время между разными серверами будете синхронизировать в случае sticky sessions?
Время логаута не нужно синхронизировать, оно в профиле пользователя
(и как вы будете решать задачу «разлогинь меня в этой сессии, не трогая остальные»?)
Если нужно предоставить пользователю возможность разлогинить любую сессию, на которую он покажет пальчиком, то и при том и том подходе нужно хранить список сессий. Если только ту, в которой пользователь прямо сейчас сидит, то у классического подхода тут преимущество. Обязательно пользуйтесь классическим подходом, если Ваша бизнес-задача — разлогинивать текущие сессии пользователей.lair
05.04.2017 14:13-1Время логаута не нужно синхронизировать, оно в профиле пользователя
… который хранится где?
Если нужно предоставить пользователю возможность разлогинить любую сессию, на которую он покажет пальчиком, то и при том и том подходе нужно хранить список сессий.
После чего совершенно не понятно, зачем гонять данные сессии на клиента.
YourChief
05.04.2017 14:31-1… который хранится где?
Который хранится в базе, а не в сессии.
После чего совершенно не понятно, зачем гонять данные сессии на клиента.
В таком случае заведите ишью в репозиториях джанги и рельсов, потребуйте немедленно удалить непонятные бэкенды для сессий.lair
05.04.2017 15:27Который хранится в базе, а не в сессии.
… которая становится той же самой единой точкой отказа. В чем разница-то?
YourChief
05.04.2017 16:27База это база, а хранилище сессий — это хранилище сессий. Предложенный хандлер устраняет SPOF в виде хранилища сессий.
lair
05.04.2017 16:30Так что мешает хранить сессии в той же БД, что и профили? Как была одна точка отказа, так и осталась.
YourChief
05.04.2017 16:32Вы когда-либо хранили сессии в БД?
lair
05.04.2017 16:32Да.
YourChief
05.04.2017 17:10Отлично. Интересно узнать ответы на два вопроса:
- Как вы удаляли старые сессии?
- Каков ваш план масштабирования пропускной способности базы на запись при росте числа сессий?
lair
05.04.2017 17:16Как вы удаляли старые сессии?
Регулярной задачей.
Каков ваш план масштабирования пропускной способности базы на запись при росте числа сессий?
Шардинг.
Напоминаю при этом, что подход с хранением сессий в куках сам по себе не полагается на базу, если приложение не использует базу вовсе, то и для хранения сессий она не нужна.
… напоминаю, что подход с хранением сессий в куках работает только при малом объеме данных в сессии.
L0NGMAN
04.04.2017 14:00Чем вам Memcached не угодила?
lexore
04.04.2017 15:28Автор статьи спросил бы у вас: Допустим, memcache упал/втупил/стал недоступен. У вас или лежит сайт, или все незалогинены.
От себя замечу, что если вам хотя бы чуть-чуть важны залогиненые пользователи, под сессии лучше использовать redis.
Там есть и сохранение на диск, и реплики, и кластер.L0NGMAN
05.04.2017 08:28-1Ведь в Memcached тоже есть реплики, кластер? А Redis ещё и имеет ряд недостатков для сессии, на пример session locking https://github.com/phpredis/phpredis/issues/37
Я не использовал Redis в highload пректе и не могу лично от себя ничего сказать…lexore
05.04.2017 15:52Да, в memcache прикрутили что-то для репликации и можно использовать несколько серверов memcached в качестве кластера.
Но данные при перезагрузке ноды вы все равно потеряете.
В highload redis отлично используется для хранения сессий, к нему обращаются php, python, go.
По вашей ссылке это проблема не redis, а конкретного php extension.
Там в обсуждении люди описали, как сделать свой handler с session locking.
Я не программист, но лично мне кажется, что session locking нужно делать в приложении, а не внутри extension, так как это специфичная фича — каждый может видеть её реализацию по разному.
vlreshet
04.04.2017 14:14-5Удивлён что ещё никто не запостил картинку с троллейбусом из буханки хлеба. Механизм cookie предназначен не для этого, с таким же приколом можно хранить сессию в localStorage. Только зачем? И кстати, вот пятой точкой чувствую что возможны баги при параллельных запросах. В такие моменты у пользователя будет возникать несколько копий сессии, каждая из которых может обработаться по-своему.
vlreshet
04.04.2017 16:06-4Минусов ляпнули — и сбежали. Хоть бы обосновали что-ли…
Aleks_ja
05.04.2017 12:28+1На самом деле на ваш вопрос «зачем» есть ответ в самом начале статьи:
На некоторой стадии развития веб-проекта возникает одна из следующих ситуаций:
backend перестаёт помещаться на одном сервере и требуется хранилище сессий, общее для всех backend-серверов
по различным причинам перестаёт устраивать скорость работы встроенных файловых сессий
Традиционно в таких случаях для хранения пользовательских сессий начинают использовать Redis, Memcached или какое-то другое внешнее хранилище. Как следствие возникает бремя эксплуатации базы данных, которая при этом не должна быть единой точкой отказа или бутылочным горлышком в системе.
Я вот никогда раньше и не задумывался, что можно так сессии хранить. Теперь обдумываю…
lexore
04.04.2017 15:33+1Замечу, что большие куки — это плохо. Несколько раз у разных подсистем (не php) ловили падения при большом объеме кук. Ловить такие ошибки на проде — всегда очень занимательная задача.
dreammaster19
04.04.2017 15:39+1Так и не понял ради чего всё это сделано? Если хотите работать с куками, то пожалуйста, зачем тогда вообще дергать сессию (а уж шифровать или нет это уже зависит от нужд). Кроме того не обязательно дергать диск или сетевые протоколы для сессий, можно хранение и в памяти сделать (если так критично к скорости чтения сессий). Кроме того, если у вас SSD, то у скорость чтения с диска довольно большая.
VolCh
04.04.2017 16:18+1Для того, чтобы перенести хранение сессий на сторону клиента без изменения механизма работы с ними.
В памяти хранить сложно, если у вас несколько инстансов апп-сервера — нужен механизм репликации (причём мастер-мастер) памяти между серверами.
dreammaster19
04.04.2017 19:05in memory nosql base
И все таки проще и логичнее просто перейти на работу с куками без сессий. С учетом того, что во многих фрейморках почти всега используются классы обертки для работы с сессиями. Просто в них использовать данные из куков. Но как пример работы через сессий через куки — довольно хороший)VolCh
04.04.2017 19:10-1Начало поста:
Традиционно в таких случаях для хранения пользовательских сессий начинают использовать Redis, Memcached или какое-то другое внешнее хранилище. Как следствие возникает бремя эксплуатации базы данных, которая при этом не должна быть единой точкой отказа или бутылочным горлышком в системе.
Однако, есть альтернатива этому подходу...Не факт, что проще и логичней, не очень хорошо и в классы-обёртки сессий фреймворков лезть, и переписывать код, если $_SESSION используется.
Interreto
04.04.2017 15:39-1Синхронизация между устройствам? Вот вы зашли на сайт с рабочего компа, затем в транспорте с планшета (еще и с 3G), что будет?
mayorovp
04.04.2017 15:43+1Ну, это болезнь любых сессий вообще, а не только данной реализации.
vlreshet
04.04.2017 16:06Но если мы храним сессию и так у себя — то нам легко раздать её на все клиенты и таким образом засинхронить. А если использовать вот это извращение с куками — тогда надо у одного устройства сессию взять, у себя её сохранить, и на другое устройство отдать. Если такой синхрон будет происходить постоянно — то вся идея ломается на корню, ибо мы опять храним сессии у себя.
mayorovp
04.04.2017 16:09+7Если раздать ее на все клиенты и таким образом засинхронить — это будет уже не сессия, а профиль пользователя.
imbasoft
04.04.2017 16:37-3Спасибо за статью идея интересная но есть пару проблемных моментов.
1. HMAC — это не электронная подпись. Поскольку строится на базе симметричного ключа и соответственно может быть подделана одной стороной без возможности это опровергнуть другой стороной. Классическая ЭП на ассиметричной криптографии этого не допускает, поскольку ЭП строится с помощью закрытого ключа который есть только у подписывающей стороны.
2. В приведенных вами исходниках ключ шифрования вшит в код, а с учетом того, то шифрование должно осуществляться на стороне клиента этот ключ может быть перехвачен третьей стороной и после чего данные будут подделаны.
Пример. Вася подключается в Web-клиенту Банка. К нему грузятся скрипты, содержащие ключ шифрования.
Вася отключается, но ключ у него есть. Вася знает что у Зины на счету много денег. Он от имени Зины строит сессию (ему даже авторизовываться не надо) и совершает перевод.
3. Вы скорее всего скажите, что сессия будет защищена TLS. Но раз так, тогда смысла в доп. шифровании нет.
Для исправления ситуации необходимо как минимум для каждой сессии получать рандомный ключ шифрования. Но даже это не защитит от атак повтором. Поэтому нужно капать в сторону использования ассиметричной крипты.mayorovp
04.04.2017 16:45+1Вы пост-то вообще читали? Сторона тут только одна — сервер, который сначала генерирует куку, а потом ее же читает.
imbasoft
04.04.2017 17:14-3Что вы называете сессией?
Если только некий id, тогда непонятно причем тут ограничения на 4кб в куку, если же в сессии присутствует состояние полное состояние клиента, то для того чтобы его нехранить на сервере его надо шифровать и оно должно шифроваться на клиенте.
В статье схемы взаимодействия не хватает.mayorovp
04.04.2017 18:07+1Веб-сессия пользователя, оно же сеанс — это понятие, объеденяющее активность пользователя по взаимодействию с веб-ресурсом с момента первого обращения к ресурсу и до прекращения взаимодействия ресурсом.
Обычно под сессией понимают термин "состояние сеанса", означающий блок нетипизированных данных, хранимых где-нибудь в течении всего сеанса, и к которым имеет доступ только серверный код.
Клиентскому же коду доступ к сессии не нужен — у него есть localStorage для тех же целей.
da411d
04.04.2017 16:43-2Я, например, генерировал id сессии хешем от юзерагента, ip, даты и секретного ключа.
И не сохранял в куках, а просто генерировал при каждом запросе.mayorovp
04.04.2017 16:46Даты чего?
da411d
04.04.2017 16:47Даты создания сессии
mayorovp
04.04.2017 16:51-1Я не понимаю. Где вы храните дату создания сессии, если id сессии генерируется на ее основе?
da411d
04.04.2017 17:26-3Дату берем сегодняшнюю.
basili4
04.04.2017 17:35+2а что будет в 23:59? Клиент разлогонится. Очень мило, можно еще сервер выключать. со словами пора спать
mayorovp
05.04.2017 09:42+1Отлично. Теперь любые два пользователя, которые сидят с одного IP и одного браузера в один день имеют общую сессию!
Вы что за сайты-то делаете, если ваша целевая аудитория, по всей видимости, бездетные незамужние люди, не сидящие за NATом?..
olku
04.04.2017 18:23-3YourChief
05.04.2017 13:17Добрый день!
Я прицельно искал такие реализации в поиске хабра, но не смог найти Вашу статью. Но сейчас я вижу, что даже найдя её, я бы её ни в коем случае не выбрал.
Во-первых, IV это не публичный ключ, и хранить его отдельно от сообщения нет смысла.
Во-вторых, нет подписи куки, и я писал в статье, почему она должна быть.olku
05.04.2017 13:26Дело хозяйское.
1. Имеет, инвалидация же.
2. Шифрование же.YourChief
05.04.2017 13:33Вы свято верите, что кука в браузере протухнет по истечении времени и тогда данные инвалидируются. Однако, клиент сам управляет куками, они ему подконтрольны полностью. Вот в том числе поэтому нужно время экспайра и его подпись. Но не только поэтому, как я уже говорил.
olku
05.04.2017 13:38Почитайте статью и гляньте код хотя бы прежде чем делать утверждения. Вектор используется для инвалидации без центрального хранилища.
YourChief
05.04.2017 13:52Я почитал ваш код и утверждаю: куки подконтрольны клиенту, какие он проставит, такие и будут. Если клиент решит их хранить вечно — в вашем случае сессия будет длиться вечно.
olku
05.04.2017 14:15Идентификатор — да, данные за ним — нет. Они не передаются клиенту, как и у файловой сессии, у которой есть собственный GC. Я, например, использую xcache с ненулевым временем жизни и вектор для ключа. Промах приводит к регенерации, никаких «вечно».
oxidmod
05.04.2017 13:45нет, если httpOnly
olku
05.04.2017 14:17можно, МИТМом, локальным прокси например, если очень надо
oxidmod
05.04.2017 18:52как ві секьюрную куку митмом прочитаете?
olku
05.04.2017 20:06Вышеуказанным методом. Например, org.littleshoot.proxy… Или что Вы хотели спросить?
oxidmod
13.04.2017 12:32-1session.cookie_secure=True session.cookie_httponly=True
?lair
13.04.2017 13:22И что? Клиент-то ее получил и прочитал, значит, и обратно послать может. Все эти флажки "безопасности" — они для добронамеренных клиентов.
oxidmod
13.04.2017 15:521. Куки с флагом HttpOnly не видны браузерному коду
2. Куки с флагом Secure пересылается только через HTTPS (HTTP с использованием SSL — Secure Socket Level)
Итак, каким образом вы организуете mitm — атаку, чтобы браузер юзера не выдал вашего вмешательства?lair
13.04.2017 15:56Куки с флагом HttpOnly не видны браузерному коду
Это, простите, как? А отправляет их кто?
Куки с флагом Secure пересылается только через HTTPS (HTTP с использованием SSL — Secure Socket Level)
Только если клиент выполняет это требование. И да, даже если клиент выполняет это требование, он может не выполняеть требование про валидацию сертификатов, или сертификаты могут быть подменены на уровне устройства и так далее, далее, далее.
oxidmod
13.04.2017 16:01Это, простите, как? А отправляет их кто?
Это значит что их не видит js.
Сам браузер их конечно шлет с каждым запросом, но js не может их не то что поменять, но даже считать.
Только если клиент выполняет это требование. И да, даже если клиент выполняет это требование, он может не выполняеть требование про валидацию сертификатов, или сертификаты могут быть подменены на уровне устройства и так далее, далее, далее.
Да, всегда есть идиоты вводящие данные карты не пойми на каких сайтах. Но вменяемый человек обратит внимание, что чтото страничка его банкинга говорит о несекьюрном конекте и предлогает продолжить на свой страх и риск.lair
13.04.2017 16:26Так вот, клиенты браузерами не ограничиваются. Поэтому если у нас есть способ куку перехватить (а если его нет, то все обсуждения, в общем-то, на пустом месте), то мы всегда можем ее отправить и сделать подмену сессии.
oxidmod
13.04.2017 16:331. Куки для API?
2. Даже если куки для API, то они защищены ssl, а значит расшифровать перехваченный трафик вы всеравно не можетеlair
13.04.2017 16:53Куки для API?
Нет, для подконтрольного агента.
Даже если куки для API, то они защищены ssl, а значит расшифровать перехваченный трафик вы всеравно не можете
Еще раз говорю: если у вас есть способ передавать куки с гарантированной невозможностью перехвата, то вы можете их использовать для чего угодно и как угодно. Инвалидация, подписание и прочий цирк нужен, когда такого способа нет по тем или иным причинам.
VolCh
14.04.2017 14:44Инвалидация нужна не только для защиты от компрометации кук и прочих токенов, но и для защиты от пользователя, который перестал быть доверенным.
lair
14.04.2017 14:46Для этого надо пользователя инвалидировать целиком.
VolCh
14.04.2017 14:49Пользователя инвалидировать не проблема, проблема инвалидировать все его открытые сессии, если принято решение на каждый запрос не проверять не запрещён ли с момента последнего запроса вход пользователю.
lair
14.04.2017 15:17Если принято такое решение, то придется сначала подумать, как вообще определить "все его открытые сессии". И так далее. Каскад решений, каждое из которых влияет на финальную функциональность.
Опять-таки, есть разница между инвалидацией куки и инвалидацией сессии.
He11ion
04.04.2017 18:31Основная и ключевая проблема на мой взгляд — инвалидация данных сессии.
Да, единая точка отказа и бутылочное горлышко в виде редиса/мемкшеа есть, но уже существует множество решений ждя больших объемов данных
Lewolf
05.04.2017 18:06Автор пишет, что у него были проблемы с поиском текущих реализаций данного механизма на PHP.
Справедливости ради, стоит отметить, что в Laravel один из драйверов сессий, которые идут с фреймворком по умолчанию, предназначен именно для хранения данных сессии в куках.
https://laravel.com/docs/master/sessionYourChief
05.04.2017 21:53Посмотрел реализацию в ларавеле:
- Громоздкая сериализация: ключи в жсон прям текстом пишутся, от всех полей в base64 паддинг ("===") остаётся, могли бы трансляцию в urlsafe base64 сделать, подпись прям в строковом виде. Можно было бы снизить расходы этой части раза в полтора точно.
- Экспайр не охвачен подписью, то есть сессии — всегда вечные.
В остальном всё очень хорошо сделано.
thecoder
13.04.2017 10:48-1С академической точки зрения интересно. Но если подумать, что такое сессия? Это некий ключ к состоянию. Хранить состояние на клиенте? Ну да, можно, если это простая корзина. Корзину даже шифровать не надо. user_id хранить даже в зашифрованном виде я бы не рискнул.
Состояние может быть очень сложным. Дополнительные сложности в отладке ради экономии 1 запроса, но платить за это жирнющей кукой — сомнительная выгода.VolCh
13.04.2017 11:48Сессия — не ключ к состоянию, а само состояние. Хранение состояния сессии на клиенте — ключ к созданию стейтлесс серверов, например в целях балансировки нагрузки и отказоустойчивости.
Merkat0r
Хоспаде… первое апреля же закончилось?