Permalink
Switch branches/tags
Nothing to show
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
248 lines (161 sloc) 16.8 KB

Куки, document.cookie

Для чтения и записи cookie используется свойство document.cookie. Однако, оно представляет собой не объект, а строку в специальном формате, для удобной манипуляций с которой нужны дополнительные функции.

Чтение document.cookie

Наверняка у вас есть cookie, которые привязаны к этому сайту. Давайте полюбуемся на них. Вот так:

alert( document.cookie );

Эта строка состоит из пар ключ=значение, которые перечисляются через точку с запятой с пробелом "; ".

Значит, чтобы прочитать cookie, достаточно разбить строку по "; ", и затем найти нужный ключ. Это можно делать либо через split и работу с массивом, либо через регулярное выражение.

Функция getCookie(name)

Следующая функция getCookie(name) возвращает cookie с именем name:

// возвращает cookie с именем name, если есть, если нет, то undefined
function getCookie(name) {
  var matches = document.cookie.match(new RegExp(
    "(?:^|; )" + name.replace(/([\.$?*|{}\(\)\[\]\\\/\+^])/g, '\\$1') + "=([^;]*)"
  ));
  return matches ? decodeURIComponent(matches[1]) : undefined;
}

Обратим внимание, что значение может быть любым. Если оно содержит символы, нарушающие форматирование, например, пробелы или ;, то оно кодируется при помощи encodeURIComponent. Функция getCookie автоматически раскодирует его.

Запись в document.cookie

В document.cookie можно писать. При этом запись не перезаписывает существующие cookie, а дополняет к ним!

Например, такая строка поставит cookie с именем userName и значением Vasya:

document.cookie = "userName=Vasya";

...Однако, всё не так просто. У cookie есть ряд важных настроек, которые очень желательно указать, так как значения по умолчанию у них неудобны.

Эти настройки указываются после пары ключ=значение, каждое -- после точки с запятой:

path=/mypath : Путь, внутри которого будет доступ к cookie. Если не указать, то имеется в виду текущий путь и все пути ниже него.

Как правило, используется `path=/`, то есть cookie доступно со всех страниц сайта.

domain=site.com : Домен, на котором доступно cookie. Если не указать, то текущий домен. Допустимо указывать текущий домен site.com и его поддомены, например forum.site.com.

Если указать специальную маску `.site.com`, то cookie будет доступно на сайте и всех его поддоменах. Это используется, например, в случаях, когда кука содержит данные авторизации и должна быть доступна как на `site.com`, так и на `forum.site.com`.

expires=Tue, 19 Jan 2038 03:14:07 GMT : Дата истечения куки в формате GMT. Получить нужную дату можно, используя объект Date. Его можно установить в любое время, а потом вызвать toUTCString(), например:

```js
// +1 день от текущего момента
var date = new Date;
date.setDate(date.getDate() + 1);
alert( date.toUTCString() );
```

Если дату не указать, то cookie будет считаться "сессионным". Такое cookie удаляется при закрытии браузера.

Если дата в прошлом, то кука будет удалена.

secure : Cookie можно передавать только по HTTPS.

Например, чтобы поставить cookie name=value по текущему пути с датой истечения через 60 секунд:

var date = new Date(new Date().getTime() + 60 * 1000);
document.cookie = "name=value; path=/; expires=" + date.toUTCString();

Чтобы удалить это cookie:

var date = new Date(0);
document.cookie = "name=; path=/; expires=" + date.toUTCString();

При удалении значение не важно. Можно его не указывать, как сделано в коде выше.

Функция setCookie(name, value, options)

Если собрать все настройки воедино, вот такая функция ставит куки:

function setCookie(name, value, options) {
  options = options || {};

  var expires = options.expires;

  if (typeof expires == "number" && expires) {
    var d = new Date();
    d.setTime(d.getTime() + expires * 1000);
    expires = options.expires = d;
  }
  if (expires && expires.toUTCString) {
    options.expires = expires.toUTCString();
  }

  value = encodeURIComponent(value);

  var updatedCookie = name + "=" + value;

  for (var propName in options) {
    updatedCookie += "; " + propName;
    var propValue = options[propName];
    if (propValue !== true) {
      updatedCookie += "=" + propValue;
    }
  }

  document.cookie = updatedCookie;
}

Аргументы:

name : название cookie

value : значение cookie (строка)

options : Объект с дополнительными свойствами для установки cookie:

expires : Время истечения cookie. Интерпретируется по-разному, в зависимости от типа:

- Число -- количество секунд до истечения. Например, `expires: 3600` -- кука на час.
- Объект типа [Date](https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/Date) -- дата истечения.
- Если expires в прошлом, то cookie будет удалено.
- Если expires отсутствует или `0`, то cookie будет установлено как сессионное и исчезнет при закрытии браузера.

path : Путь для cookie.

domain : Домен для cookie.

secure : Если true, то пересылать cookie только по защищенному соединению.

Функция deleteCookie(name)

Здесь всё просто -- удаляем вызовом setCookie с датой в прошлом.

function deleteCookie(name) {
  setCookie(name, "", {
    expires: -1
  })
}

Сторонние cookie

При работе с cookie есть важная тонкость, которая касается внешних ресурсов.

Теоретически, любой ресурс, который загружает браузер, может поставить cookie.

Например:

  • Если на странице есть <img src="http://mail.ru/counter.gif">, то вместе с картинкой в ответ сервер может прислать заголовки, устанавливающие cookie.
  • Если на странице есть <iframe src="http://facebook.com/button.php">, то во-первых сервер может вместе с button.php прислать cookie, а во-вторых JS-код внутри ифрейма может записать в document.cookie

При этом cookie будут принадлежать тому домену, который их поставил. То есть, на mail.ru для первого случая, и на facebook.com во втором.

Такие cookie, которые не принадлежат основной странице, называются "сторонними" (3rd party) cookies. Не все браузеры их разрешают.

Как правило, в настройках браузера можно поставить "Блокировать данные и файлы cookie сторонних сайтов" (Chrome).

В Safari такая настройка включена по умолчанию и выглядит так:

Тс-с-с. Большой брат смотрит за тобой.

Цель этого запрета -- защитить посетителей от слежки со стороны рекламодателей, которые вместе с картинкой-баннером присылают и куки, таким образом помечая посетителей.

Например, на многих сайтах стоят баннеры и другая реклама Google Ads. При помощи таких cookie компания Google будет знать, какие именно сайты вы посещаете, сколько времени вы на них проводите и многое другое.

Как? Да очень просто -- на каждом сайте загружается, к примеру, картинка с рекламой. При этом баннер берётся с домена, принадлежащего Google. Вместе с баннером Google ставит cookie со специальным уникальным идентификатором.

Далее, при следующем запросе на баннер, браузер пошлёт стандартные заголовки, которые включают в себя:

  • Cookie с домена баннера, то есть уникальный идентификатор, который был поставлен ранее.
  • Стандартный заголовок Referrer (его не будет при HTTPS!), который говорит, с какого сайта сделан запрос. Да, впрочем, Google и так знает, с какого сайта запрос, ведь идентификатор сайта есть в URL.

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

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

Возможно, компания Apple, которая выпустила Safari, поставила такой флаг по умолчанию именно для уменьшения влияния Google?

А если очень надо?

Итак, Safari запрещает сторонние cookie по умолчанию. Другие браузеры предоставляют такую возможность, если посетитель захочет.

А что, если ну очень надо поставить стороннюю cookie, и чтобы это было надёжно?

Такая задача действительно возникает, например, в системе кросс-доменной авторизации, когда есть несколько доменов 2-го уровня, и хочется, чтобы посетитель, который входит в один сайт, автоматически распознавался во всей сетке. При этом cookie для авторизации ставятся на главный домен -- "мастер", а остальные сайты запрашивают их при помощи специального скрипта (и, как правило, копируют к себе для оптимизации, но здесь это не суть).

Ещё пример -- когда есть внешний виджет, например, iframe с информационным сервисом, который можно подключать на разные сайты. И этот iframe должен знать что-то о посетителе, опять же, авторизация или какие-то настройки, которые хорошо бы хранить в cookie.

Есть несколько способов поставить 3rd-party cookie для Safari.

Использовать ифрейм. : Ифрейм является полноценным окном браузера. В нём должна быть доступна вся функциональность, в том числе cookie. Как браузер решает, что ифрейм "сторонний" и нужно запретить для него и его скриптов установку cookie? Критерий таков: "в ифрейме нет навигации". Если навигация есть, то ифрейм признаётся полноценным окном.

Например, в сторонний `iframe` можно сделать POST. И тогда, в ответ на POST, сервер может поставить cookie. Или прислать документ, который это делает. Ифрейм, в который прошёл POST, считается родным и надёжным.

Popup-окно : Другой вариант -- использовать popup, то есть при помощи window.open открывать именно окно со стороннего домена, и уже там ставить cookie. Это тоже работает.

Редирект : Ещё одно альтернативное решение, которое подходит не везде - это сделать интеграцию со сторонним доменом, такую что на него можно сделать редирект, он ставит cookie и делает редирект обратно.

Дополнительно

  • На Cookie наложены ограничения:

    • Имя и значение (после encodeURIComponent) вместе не должны превышать 4кб.
    • Общее количество cookie на домен ограничено 30-50, в зависимости от браузера.
    • Разные домены 2-го уровня полностью изолированы. Но в пределах доменов 3-го уровня куки можно ставить свободно с указанием domain.
    • Сервер может поставить cookie с дополнительным флагом HttpOnly. Cookie с таким параметром передаётся только в заголовках, оно никак не доступно из JavaScript.
  • Иногда посетители отключают cookie. Отловить это можно проверкой свойства navigator.cookieEnabled

    if (!navigator.cookieEnabled) {
      alert( 'Включите cookie для комфортной работы с этим сайтом' );
    }

    ...Конечно, предполагается, что включён JavaScript. Впрочем, посетитель без JS и cookie с большой вероятностью не человек, а бот.

Итого

Файл с функциями для работы с cookie: cookie.js.