Как куки передаются от браузера серверу
- аутентификации пользователя;
- хранения персональных предпочтений и настроек пользователя;
- отслеживания состояния сеанса доступа пользователя;
- ведения статистики о пользователях.
Содержание
История
Формат и синтаксис cookie
Дополнительные сведения
Заблуждения
С момента появления куки, в СМИ и Интернете начали распространяться различные слухи. В 1998 году компьютерный отдел Министерства энергетики Соединенных Штатов (CIAC) заявил, что опасности куки не представляют, и пояснил, что «информация о том, откуда вы приходите и какие веб-страницы посещаете, и так сохраняется в лог-файлы веб-серверов».[14] В 2005 году были опубликованы результаты исследования, согласно которому значительный процент респондентов уверен, что:
- куки, как черви и вирусы, могут стереть данные с жёсткого диска пользователя;
- куки являются причиной всплывающих окон;
- куки используются для почтового спама;
- куки используются только для рекламы.
В действительности же, куки представляют собой лишь данные, а не программный код: они не могут стереть или прочитать информацию с компьютера пользователя. Однако куки позволяют проследить, какие веб-страницы просмотрены пользователем на данном сайте, и эта информация может быть сохранена в профиле пользователя. Такие профили зачастую анонимны и не содержат личной информации пользователей (имя, адрес и т. д.). Точнее, они не могут её содержать, пока пользователь не сделал эту информацию доступной. Но даже несмотря на анонимность, эти профили стали предметом споров о сохранении приватности. Пример 1. Управление подмножеством документов, для которых действительны значения cookie, и их сроком годности Браузер запрашивает документ и принимает от сервера в ответ:
Когда браузер запрашивает URL с путем "/" на этом сервере, он посылает серверу:
Браузер запрашивает документ и принимает от сервера в ответ:
Когда браузер запрашивает URL с путем "/" на этом сервере, он посылает серверу уже два значения cookie:
Сервер установил еще одно значение cookie, на этот раз с другой областью действия:
Теперь браузер, запрашивая URL с путем "/" на этом сервере, посылает лишь два значения cookie:
и лишь при запросе браузером документов с путем "/foo" на этом сервере посылаются все три значения cookie:
поскольку только для него установлен срок годности - 9 ноября 1999 года. Все остальные значения не будут сохранены.
Способы задания значений cookie
Способ задания значений cookie зависит того, как эти значения будут использоваться и какие имеются серверные ресурсы. Можно манипулировать временем жизни выставленных cookie и устанавливать подмножества URL (Universal Resource Locator), в которых заданные значения действительны. Есть несколько способов задания, наиболее часто используются три - через META-таги языка HTML, JavaScript и CGI-скрипты. Любым способом можно задавать как одно, так и несколько значений сразу. Сразу хочу предупредить - не забывайте об ограничениях по объему и количеству значений cookie, а также параметре domain, так как помимо основного доменного имени узла часто бывает несколько алиасов (alias) [3] . 1. Задание cookie с помощью META-тагов Простейший способ выставить cookie - использовать соответствующий META-таг в контейнере
. любого статического HTML документа. В общем случае это выглядит следующим образом: Такой способ задания cookie, на мой взгляд, наиболее интересен для создателей маленьких домашних страничек, когда нет возможности писать свои собственные CGI-скрипты. А если есть поддержка SSI(Server Side Include) или PHP/Fi, то можно делать интерактивные страницы вообще без использования внешних CGI-скриптов. При наличии SSI на узле создание интерактивности с использованием механизма cookie становится просто удовольствием. Функция чтения значения cookie Возвращает установленное значение или пустую строку, если cookie не существует.Приватность и сторонние куки.
- пользователю предоставляется информация о том, как эти данные используются;
- пользователь имеет возможность отказаться от этого.
Тем не менее, в данной статье также говорится, что хранение технически необходимых данных освобождается от этих норм. Ожидалось, что директива вступит в силу с октября 2003 года, но доклад от декабря 2004 года отмечает, что эти положения не нашли применения на практике и что в некоторых государствах (Словакия, Латвия,Греция, Бельгия и Люксембург) эти положения не внесены в национальные законодательства. Доклад предлагает провести тщательный анализ ситуации в государствах, участвующих в договоре. Спецификация P3P включает возможность для веб-сервера сообщить браузеру о нарушении конфиденциальности, указывая характер собираемой информации и цели сбора. Сюда входит и использование информации, полученной с помощью куки. По спецификации P3P браузер может принимать или отклонять куки согласно пользовательским настройкам или же спросить пользователя. Многие веб-браузеры, включая Safari от Apple и Internet Explorer версий 6 и 7 от Microsoft, поддерживают спецификации P3P, которые позволяют определить, следует ли разрешать сторонние куки. Веб-браузер Opera позволяет пользователям отказаться от сторонних куки и создать глобальные или выборочные профили безопасности для веб-доменов. Firefox 2 был лишён этой опции, но она была восстановлена в версии 3.
Недостатки
Помимо проблем конфиденциальности, куки имеют и некоторые технические недостатки. В частности, они не всегда точно идентифицируют пользователя и могут быть причиной атак злоумышленников.
Cookies are mainly used for three purposes:
Logins, shopping carts, game scores, or anything else the server should remember
User preferences, themes, and other settings
Recording and analyzing user behavior
Cookies were once used for general client-side storage. While this made sense when they were the only way to store data on the client, modern storage APIs are now recommended. Cookies are sent with every request, so they can worsen performance (especially for mobile data connections). Modern APIs for client storage are the Web Storage API ( localStorage and sessionStorage ) and IndexedDB.
The Set-Cookie and Cookie headers
Note: Here's how to use the Set-Cookie header in various server-side applications:
Define the lifetime of a cookie
The lifetime of a cookie can be defined in two ways:
Note: When you set an Expires date and time, they're relative to the client the cookie is being set on, not the server.
Here's an example:
Domain attribute
The Domain attribute specifies which hosts can receive a cookie. If unspecified, the attribute defaults to the same host that set the cookie, excluding subdomains. If Domain is specified, then subdomains are always included. Therefore, specifying Domain is less restrictive than omitting it. However, it can be helpful when subdomains need to share information about a user.
Path attribute
The Path attribute indicates a URL path that must exist in the requested URL in order to send the Cookie header. The %x2F ("/") character is considered a directory separator, and subdirectories match as well.
For example, if you set Path=/docs , these request paths match:
But these request paths don't:
SameSite attribute
Here's an example:
Cookie prefixes
Because of the design of the cookie mechanism, a server can't confirm that a cookie was set from a secure origin or even tell where a cookie was originally set.
A vulnerable application on a subdomain can set a cookie with the Domain attribute, which gives access to that cookie on all other subdomains. This mechanism can be abused in a session fixation attack. See session fixation for primary mitigation methods.
As a defense-in-depth measure, however, you can use cookie prefixes to assert specific facts about the cookie. Two prefixes are available:
If a cookie name has this prefix, it's accepted in a Set-Cookie header only if it's marked with the Secure attribute and was sent from a secure origin. This is weaker than the __Host- prefix.
Note: On the application server, the web application must check for the full cookie name including the prefix. User agents do not strip the prefix from the cookie before sending it in a request's Cookie header.
For more information about cookie prefixes and the current state of browser support, see the Prefixes section of the Set-Cookie reference article.
JavaScript access using Document.cookie
Please note the security issues in the Security section below. Cookies available to JavaScript can be stolen through XSS.
Security
Tracking and privacy
Cookie-related regulations
- The General Data Privacy Regulation (GDPR) in the European Union
- The ePrivacy Directive in the EU
- The California Consumer Privacy Act
These regulations have global reach. They apply to any site on the World Wide Web that users from these jurisdictions access (the EU and California, with the caveat that California's law applies only to entities with gross revenue over 25 million USD, among things).
These regulations include requirements such as:
Other ways to store information in the browser
Куки часто ипользуются для:
- Управления сеансом (логины, корзины для виртуальных покупок)
- Персонализации (пользовательские предпочтения)
- Трекинга (отслеживания поведения пользователей)
До недавнего времени куки использовались в качестве хранилища информации на стороне пользователя. Это могло иметь смысл в отсутствии вариантов, но теперь, когда в распоряжении браузеров появились различные API для хранения данных, это уже не так. Из-за того что куки пересылаются с каждым запросом, они могут ухудшать производительность (особенно при использовании мобильных сетей). В качестве хранилищ данных на стороне пользователя вместо них можно использовать Web storage API ( localStorage и sessionStorage ) и IndexedDB.
Чтобы посмотреть сохранённые куки и другие хранилища данных, которые использует веб-страница, можно использовать Storage Inspector (Инспектор хранилища) в инструментах разработчика.
Создание куки
Заголовки Set-Cookie и Cookie
Этот заголовок с сервера даёт клиенту указание сохранить куки (это делают, например, PHP, Node.js, Python и Ruby on Rails). Ответ, отправляемый браузеру, содержит заголовок Set-Cookie , и куки запоминается браузером.
Теперь с каждым новым запросом к серверу при помощи заголовка Cookie (en-US) браузер будет возвращать серверу все сохранённые ранее куки.
Сессионные cookie
Простой cookie, пример которого приведён выше, представляет собой сессионный cookie (session cookie) - такие cookie удаляются при закрытии клиента, то есть существуют только на протяжении текущего сеанса, поскольку атрибуты Expires или Max-Age для него не задаются. Однако, если в браузере включено автоматическое восстановление сеанса, что случается очень часто, cookie сеанса может храниться постоянно, как если бы браузер никогда не закрывался.
Область видимости куки
Директивы Domain и Path определяют область видимости куки, то есть те URL-адреса, к которым куки будут отсылаться.
Атрибут Domain
Атрибут Domain указывает хосты, на которые отсылаются куки. Если он не задан, то по умолчанию берётся доменная часть адреса документа (но без поддоменов). Если домен указан явно, то поддомены всегда включены.
Атрибут Path
Атрибут Path указывает URL, который должен быть в запрашиваемом ресурсе на момент отправки заголовка Cookie . Символ %x2F ("/") интерпретируется как разделитель в URL-пути, подпути также будут учитываться.
Если задан Path=/docs , то совпадать будут следующие пути:
А эти пути совпадать не будут:
Куки SameSite
С атрибутом Strict куки будут отправляться только тому сайту, которому эти куки принадлежат. Атрибут Lax работает похоже, но куки будут отправляться также при навигации на тот сайт, которому принадлежат куки. Например, при переходе по ссылке с внешнего сайта. Атрибут None отключает ограничение на отправку кук для межсайтовых запросов, но только в безопасном контексте (то есть если установлен SameSite=None , тогда также должен быть установлен атрибут Secure ). Если атрибут SameSite не установлен, куки будут восприниматься как Lax .
В таблице совместимости вы можете найти информацию о том, как обрабатываются атрибуты в конкретных версиях браузеров.
Куки с префиксами
Из-за дизайна механизма кук сервер не может подтвердить, что куки были отправлены с защищённого источника (secure origin), или быть уверенным в том, где именно они были установлены.
Уязвимое приложение поддомена может установить куку с атрибутом Domain , тем самым открывая к ней доступ на всех других поддоменнах. Этот механизм может быть проэксплуатирован с атакой фиксация сессии.
Ознакомьтесь со статьёй фиксация сессии, чтобы узнать об основных методах защиты от этой атаки.
Тем не менее в соответствии с принципом защита в глубину вы можете использовать куки с префиксами, чтобы гарантировать специфические факты о куках. Доступны два префикса:
Если в куке содержится этот префикс, она будет установлена заголовком Set-Cookie только в том случае, если кука будет содержать атрибут Secure и если запрос будет отправляться из защищённого источника. Также кука не должна включать атрибут Domain и должна содержать атрибут Path со значением / .
Если в куке содержится этот префикс, она будет установлена заголовком Set-Cookie только в том случае, если кука будет содержать атрибут Secure и если запрос будет отправляться из защищённого источника. Защита с помощью этого префикса слабее по сравнению с префиксом __Host- .
Браузеры будут откланять установку этих кук, если они не будут удовлетворять всем ограничениям. Заметьте, что куки с префиксами, созданные в рамках поддомена, будут ограничиваться только им или будут полностью игнорироваться. Так как бэкенд проверяет только куки с заранее известными именами при авторизации пользователя или валидации CSRF-токена, куки с префиксами фактически работают как защитный механизм от фиксации сессии.
Для получения информации о статусе поддержки префиксов в разных браузерах обратитесь к статье про Set-Cookie .
Доступ из JavaScript с помощью Document.cookie
Пожалуйста, учитывайте вытекающие из этого проблемы, про которые рассказывается ниже в разделе Безопасность. Куки, доступные для JavaScript, могут быть похищены посредством XSS.
Безопасность
При сохранении информации в куках имейте в виду, что у всех пользователей есть возможность просматривать и изменять их значения. В зависимости от типа приложения вы можете использовать ни о чём не говорящее имя для идентификатора кук, смысл которого будет понятен только бэкенду. Также вы можете рассмотреть возможность использования альтернативных механизмов аутентификации и конфиденциальности, например, JSON Web Tokens
Способы предотвращения атак, использующих куки:
Захват сессии (session hijacking) и XSS
Куки часто используются в веб-приложениях для идентификации аутентифицированного пользователя и сеанса работы. Соответственно, похищение кук из приложения может привести к захвату авторизованного сеанса пользователя. Кража кук часто осуществляется посредством социальной инженерии (Social Engineering) и использования уязвимости XSS (en-US).
Межсайтовая подделка запроса (CSRF - Cross-site request forgery)
Если вы аутентифицированны в своём банковском аккаунте, а куки по-прежнему действительны (и никакой дополнительной проверки не требуется), то при загрузке HTML-документа форума или чата с этим изображением деньги будут переведены с вашего счета. Для защиты от этого используется ряд методов:
- Как и при XSS (en-US), важна фильтрация входящей информации.
- Для любой чувствительной операции должно запрашиваться подтверждение.
- Куки, используемые для чувствительных операций, должны иметь короткий срок действия.
- Дополнительную информацию можно получить в пользовательской инструкции по предотвращению CSRF на сайте OWASP.
Трекинг и приватность
Сторонние (Third-party) куки
Сервер, хостящий страницу, устанавливает собственные куки, но на странице могут находиться изображения и другие компоненты с других доменов (например, баннерная реклама), они в свою очередь могут устанавливать сторонние куки. Сторонние куки часто используются для рекламы и трекинга пользователей в сети. Как пример, можете посмотреть куки, которые устанавливает Google.
Третья сторона, контролирующая внедрение сторонних кук, может создать профиль пользователя на основе истории его посещений разных сайтов с помощью кук, отправляемых одним и тем же браузером с разных сайтов. Firefox по умолчанию блокирует сторонние куки, про которые известно, что они используются для трекинга пользователей. Сторонние куки (или просто куки для трекинга) могут также быть заблокированы другими настройками браузера или расширениями. Блокировка кук в некоторых ситуациях может стать причиной некорректного поведения сторонних компонентов, например, виджетов социальных сетей.
Бэкенд может (и должен) устанавливать у кук атрибут SameSite для управления отправкой кук на сторонние серверы.
Законодательство, связанное с куки
Регулирующие акты и законодательство, покрывающие куки, включают:
- General Data Privacy Regulation (GDPR) в Европейском Союзе
- ePrivacy Directive в Европейском Союзе
- California Consumer Privacy Act в Штате Калифорния
Эти акты и директивы действуют глобально. Они применяются ко всем сайтам во Всемирной паутине, к которым пользователи из данных юрисдикций получают доступ (Европейский Союз и Калифорния, с оговоркой, что Калифорнийский закон применяется к компаниям с доходом выше 25 миллионов долларов и несколькими другими оговорками).
Эти акты и директивы включают такие требования как:
- Сообщать пользователям, что сайт использует куки.
- Давать возможность пользователям отказываться от получения всех или некоторых кук.
- Давать возможность пользователям использовать основые функции вашего сервиса без получения кук.
Могут существовать другие законодательные акты, которые применимы к вашей локальной юрисдикции. На вас лежит ответственность знать про них и следовать им. Существуют компании, которые предлагают код с "куки баннером" и берут на себя заботы о следовании законодательству, связанному с куками.
Другие способы хранения информации в браузере
Другой способ для хранения данных в браузере — Web Storage API. Свойства window.sessionStorage и window.localStorage подобны сессионным и постоянным кукам, но позволяют хранить больше данных и никогда не отправляются на сервер. Для хранения ещё большего объёма структурированных данных может использоваться IndexedDB API или библиотеки, построенные поверх него.
Существуют техники для повторной установки кук после их удаления. Такие куки называются куки-зомби. Эти техники нарушают принципы приватности пользователей и пользовательского контроля и могут нарушать законодательства, регулирующие приватность данных, соотвественно, использующий их сайт подвержен судебному разбирательству.
Вы когда-нибудь работали с куки? Казалось ли вам при этом, что их использование организовано просто и понятно? Полагаю, что в работе с куки есть множество нюансов, о которых стоит знать новичкам.
Свойство document.cookie
Взглянем на классический способ работы с куки. Соответствующая спецификация существует, благодаря Netscape, с 1994 года. Компания Netscape реализовала свойство document.cookie в Netscape Navigator в 1996 году. Вот определение куки из тех времён:
Главу про document.cookie даже можно найти во втором издании книги «Javascript. The Definitive Guide», которое вышло в январе 1997 года. Это было 24 года тому назад. И мы всё ещё пользуемся тем же старым способом работы с куки ради обратной совместимости.
Как же это выглядит?
Получение куки
Да, именно так всё и делается. В нашем распоряжении оказывается строка со всеми значениями, хранящимися в куки-файле, разделёнными точкой с запятой.
Как вытащить из этой строки отдельное значение? Если вам кажется, что для этого надо самостоятельно разбить строку на части — знайте, что так оно и есть:
Как узнать о том, когда истекает срок действия какого-нибудь из куки? Да, в общем-то, никак.
А как узнать домен, с которого установлен какой-нибудь куки? Тоже никак.
Установка куки
Вышеприведённая команда позволяет создать куки с именем theme , значением которого является dark . Хорошо. Значит ли это, что document.cookie — это строка. Нет, не значит. Это — сеттер.
Эта команда не перезапишет куки с именем theme . Она создаст новый куки с именем mozilla . Теперь у нас имеются два куки.
По умолчанию срок действия куки, созданного так, как показано выше, истекает после закрытия браузера. Но при создании куки можно указать срок его действия:
Да. Это — как раз то, что мне нужно — подбирать дату и время истечения срока действия куки в формате GMT каждый раз, когда нужно установить куки. Ладно, давайте воспользуемся для решения этой задачи JavaScript-кодом:
Но, к счастью, у нас есть и другой способ установки момента истечения срока действия куки:
Свойство max-age представляет собой срок «жизни» куки в секундах. Соответствующее значение имеет более высокий приоритет, чем то, которое задано с помощью свойства expires .
И не надо забывать о свойствах path и domain . По умолчанию куки устанавливаются для текущего расположения и текущего хоста. Если надо установить куки для всего домена — надо будет добавить к команде установки куки конструкцию такого вида:
Удаление куки
Для удаления куки надо установить дату и время истечения срока действия куки на какой-нибудь момент из прошлого. Для того чтобы всё точно сработало бы — тут рекомендуется использовать начало эпохи Unix.
Работа с куки в сервис-воркерах
Это просто невозможно. Дело в том, что работа с document.cookie — это синхронная операция, в результате воспользоваться ей в сервис-воркере нельзя.
Cookie Store API
Существует черновик стандарта одного замечательного API, направленного на работу с куки, который способен значительно облегчить нам жизнь в будущем.
Во-первых — это асинхронный API, а значит — пользоваться им можно, не блокируя главный поток. Применять его можно и в сервис-воркерах.
Во-вторых — этот API устроен гораздо понятнее, чем существующий механизм работы с куки.
▍Получение куки
Метод getAll возвращает массив, а не строку. Именно этого я и жду, когда пытаюсь получить некий список.
А вот — приятная неожиданность. Можно узнать и дату истечения срока действия куки, и сведения о домене и пути, и при этом не пользоваться никакими хаками.
▍Установка куки
Мне очень нравится этот синтаксис!
▍Удаление куки
Или можно, как раньше, установить дату истечения срока действия куки на некий момент в прошлом, но не вижу причины поступать именно так.
▍События куки
Как видите, теперь у нас есть возможность подписываться на изменения куки, не занимаясь опросом document.cookie , блокирующим главный поток. Фантастика!
▍Сервис-воркеры
Можно ли пользоваться этим API прямо сейчас?
Хотя этим API уже можно пользоваться, но тут надо проявлять осторожность. Cookie Store API работоспособно в Chrome 87+ (Edge 87+, Opera 73+). В других браузерах можно воспользоваться полифиллом, который, правда, не возвращает полной информации о куки, как это сделано в настоящем API. Прогрессивные улучшения — это вещь.
И учитывайте, что спецификация этого API всё ещё находится в статусе «Draft Community Group Report». Но если в вашем проекте важен хороший «опыт разработчика» — попробуйте современный способ работы с куки.
Любая технология решает конкретные задачи с учётом имеющихся ограничений. Эти задачи и ограничения полностью определяют её облик, потому вопрос «зачем нужно» правильнее рассмотреть раньше вопроса «как работает».
Повторная установка cookie с таким же идентификатором приводит к замещению старого значения вместе со всеми атрибутами. Специальной процедуры для удаления cookie нет, для этого достаточно установить новое значение с expires в прошлом или с нулевым max-age .
Работа с cookie на Perl
На пятый запуск скрипт вернёт ответ:
За полным описанием функциональности модуля обращайтесь документации и исходному коду.
Работа с cookie на PHP
Обратите внимание, что PHP некоторых версий по умолчанию экранирует специальные символы (в частности кавычки) во входных данных добавлением перед ними \ . Это сделано для защиты плохо написанного кода, формирующего из непроверенных данных SQL -запросы, от SQL-инъекций. Получить признак активности режима можно посредством вызова функции get_magic_quotes_gpc , а выполнить обратное преобразование — с помощью stripslashes . Хотя с версии PHP 5.4.0 этот бред был удалён, проверку лучше оставить для совместимости.
- $cookie_str = $_SERVER [ 'HTTP_COOKIE' ];
- $rus = $_COOKIE [ 'rus' ];
- $counter = $_COOKIE [ 'counter' ];
- if (! $counter ) $counter = 0 ;
- $array = $_COOKIE [ 'array' ];
- if (! $array )
- $array = array( 'a' , 'b&c' );
- >else
- if (get_magic_quotes_gpc())
- $array = stripslashes( $array );
- $array = unserialize( $array );
- >
- setcookie( 'counter' , $counter + 1 ,
- time() + 3600 * 24 * 30 * 3 , NULL, NULL, false, true);
- setcookie( 'rus' , 'Ура!' );
- $array [] = $counter ;
- setcookie( 'array' , serialize( $array ));
- echo "Cookie HTTP header: $cookie_str\n" ;
- echo "rus: $rus, counter: $counter, array: " .
- join( ' ' , $array );
- ?>
Вывод скрипта аналогичен приведенному ранее выводу Perl-скрипта за исключением различий в виде сериализованного массива.
Клиентский JavaScript API: document.cookie
Читайте также: