Перейти к содержимому

Состояние посетителя в куках - запись, чтение, защищённая кука

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

Решение

Куки в платформе читают и пишут через объекты запроса и ответа. Прямые функции языка работают, но проходят мимо префикса имени, настроек домена и общих правил проекта.

Записываем значение:

use Bitrix\Main\Context;
use Bitrix\Main\Web\Cookie;
$cookie = new Cookie('CITY_CODE', 'msk', time() + 86400 * 30);
$cookie->setHttpOnly(true); // недоступна скриптам страницы
Context::getCurrent()->getResponse()->addCookie($cookie);

Кука уходит вместе с ответом, поэтому её ставят до вывода страницы. Срок жизни задают явно: без него значение исчезнет сразу при закрытии браузера.

Читаем значение:

$request = Context::getCurrent()->getRequest();
$city = (string)$request->getCookie('CITY_CODE');
// платформа сама подставит префикс имени из настроек главного модуля
// пустое значение означает и отсутствие куки, и пустую строку в ней

Чтение через объект запроса избавляет от догадок про префикс имени. Он задаётся в настройках главного модуля и на разных проектах заметно отличается.

Закрываем чувствительное значение:

use Bitrix\Main\Web\CryptoCookie;
$cookie = new CryptoCookie('CLIENT_HASH', $hash);
Context::getCurrent()->getResponse()->addCookie($cookie);
echo $request->getCookie('CLIENT_HASH'); // чтение расшифровывает само
// ошибка расшифровки означает подмену куки или другой ключ шифрования

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

Учитываем размер и предел браузера:

// длинное значение ядро разобьёт на части автоматически
// CLIENT_HASH хранит ссылку, CLIENT_HASH_0 и далее - фрагменты
printf("длина значения: %d\n", strlen($hash));
// в куках держат идентификаторы и коды, а не наборы данных

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

Выбор посетителя, влияющий на вывод страницы, обязательно учитывают в ключе кэша. Город в куке и кэш без учёта города дают классическую картину: половина посетителей видит чужие цены и чужие склады.

Типичные проблемы

Кука ставится, но при следующем запросе её нет.

Не задан срок жизни куки, и её значение живёт до закрытия браузера. Срок жизни куки задают явно при её создании в коде страницы.

Значение читается не тем именем.

В настройках модуля задан префикс имён кук, а чтение идёт напрямую из массива запроса. Через объект запроса нужный префикс имени куки подставляется сам.

Посетители видят чужой город и чужие цены.

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

Значение куки подменяют через инструменты браузера.

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

Кука не ставится на части страниц.

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

Частые вопросы

Чем кука отличается от сеанса?

Кука хранится у посетителя и приходит с каждым запросом, сеанс живёт на сервере и привязан к идентификатору. В куке держат мелкие значения, в сеансе - временные данные работы.

Когда нужна защищённая кука?

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

Что нельзя хранить в куках?

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

Почему выбор города сбрасывается?

Кука без срока жизни исчезает при закрытии браузера, а кука с другим доменом не приходит на поддомен. Проверяют оба параметра.

Как связать куку с кэшем страницы?

Добавить её значение в ключ кэша компонента или пометить блок динамическим в композите. Иначе кэш отдаст всем один и тот же вариант.

Смежное

Первоисточник