HTTP Cookie
Розв'язувані завдання
Будь-яка технологія - це інструмент для вирішення конкретних завдань з урахуванням практичних обмежень. Ці завдання та обмеження повністю визначають технічні деталі реалізації, тому питання «навіщо потрібно» правильніше розглянути раніше питання «як працює».
Cookie у протоколі HTTP
- це механізм керування станом у транспортному протоколі HTTP [RFC 6265]. Він будується передачі web-сервером іменованих значень з додатковими атрибутами клієнту і повернення їх у сервер під час наступних запитів. Передані імена зі значеннями та атрибутами називаються . На клієнт вони приходять в HTTP-відповіді сервера в заголовках Set-Cookie за кількістю cookies, що передаються, а на сервер повертаються в запиті, упаковані в заголовок Cookie .
- # Заголовки чергового запиту до сервера
- # Містять раніше отримані від сервера cookies
- GET/HTTP/1.1
- User-Agent: Opera/9.80 (Windows NT 6.1) Version/12.16
- Host: falstart.com
- Accept: text/html, */*;q=0.1
- Accept-Language: ru, ru-RU; q = 0.9, en; q = 0.8
- Accept-Encoding: gzip, deflate
- Cookie: s ># Заголовки відповіді сервера клієнту
- HTTP/1.1 200 OK
- Server: nginx/0.7.61
- Date: Thu, 03 Oct 2013 16:21:24 GMT
- Content-Type: text/html; charset=utf-8
- Transfer-Encoding: chunked
- Set-Cookie: s &Set-Cookie: name=user; path=/; expires=Sat, 05-Oct-2013 16:21:24 GMT
встановлюються сервером для керування обробкою cookies на клієнті. Всі вони опціональні і мають значення за замовчуванням:
На клієнті, на відміну від сервера, cookies ідентифікується не тільки ім'ям, його доповнюють значення domain і path: браузер цілком може зберігати різні cookiesоднаковими іменами та передавати їх серверу, навіть одночасно. Будьте уважні: повторне встановлення cookie з новим domain або path приведе до створення дубля, не змінюйте ці атрибути протягом життя cookie.
Повторне встановлення cookie з таким самим ідентифікатором призводить до заміщення старого значення разом з усіма атрибутами. Спеціальної процедури для видалення cookie немає, для цього достатньо встановити нове значення з expires у минулому або з нульовим max-age.
Під час роботи з cookies необхідно враховувати низку обмежень. Суворих обмежень на їх розмір і число немає, RFC 6265 встановлює лише вимоги до web-браузеру: він повинен зберігати від 50 значень cookies на домен з розміром кожного разом із атрибутами від 4096 байт. Браузер може зберігати і більший обсяг значень, але безпечно триматися в рамках гарантованих лімітів. При створенні програми слід враховувати сценарії несподіваного видалення, будь-якої модифікації або додавання нових значень cookies: дані, що прийшли в cookies, анітрохи не безпечніші за дані з web-форм.
Отримання та встановлення cookies на сервері
Cookies доставляються у звичайних заголовках HTTP, тому в найпростішому випадку для роботи з ними можна використовувати відповідний API мови програмування. Але робити так слід лише вивчення механізму чи за відсутності більш підходящих коштів. В інших випадках ручний розбір та формування заголовків програють за якістю спеціалізованим бібліотечним реалізаціям.
По можливості використовуйте спеціалізовані бібліотеки, більшість із них реалізують логіку кодування та декодування рядків cookies відповідно до рекомендацій RFC 6265 та обробки відповідних HTTP-заголовків. Далі розглянемо інструменти роботи з cookies,надані поширеними мовами програмування.
Так як cookies приходять на клієнт у HTTP-заголовках, цілком природно, що їх установка повинна виконуватися раніше за початок передачі тіла відповіді. Це обмеження є справедливим для будь-яких мов програмування.
Робота з cookie на Perl
У стандартне постачання Perl входить модуль CGI::Cookie . Їм можна користуватися як безпосередньо, так і за допомогою функції обгортки cookie з модуля CGI, що підтримує процедурний та об'єктно-орієнтований інтерфейси. Залежно від аргументів, функція розбирає вхідні cookies або формує вихідні. Модуль приховує деталі кодування заголовків та надає розробнику простий інтерфейс. Корисна особливість - підтримка передачі в cookies Perl-об'єктів (масивів та хешей) крім скалярних значень.
- #!/usr/bin/perl
- usestrict;
- useCGIqw(cookie); # Імпорт функції cookie
- useCGI::Fast;
- while ( $ q = CGI::Fast->new)
- # Сире значення HTTP-заголовка Cookie
- # (не призначено для ручного розбирання)
- $cookie_str = $ENV;
- ## Отримання декодованих скалярів та об'єктів із запиту
- # Виклик cookie як методу в ООП-стилі
- $ rus = $ q ->cookie( 'rus' );
- $counter = $q ->cookie('counter') 0;
- # Виклик cookie як функції у процедурному стилі
- @array = cookie('array');
- @array = ('a', 'b&c') unless @array;
- # Підготовка значень передачі клієнту
- $out_counter = $q ->cookie(
- -name => 'counter',
- -value => $ counter + 1 ,
- -expires => '+3M' , # Зберігати 3 місяці з моменту встановлення
- -httponly => 1
- );
- $out_ukr = $q->cookie( 'rus' => 'Ура!' );
- $out_array = cookie(
- -name => 'array',
- -value => [@array, $counter]
- );
- ## Включення cookies до заголовків відповіді
- print $ q ->header( -cookie => [ $ out_counter , $ out_rus , $ out_array ] );
- print "Cookie HTTP header:$cookie_str \n" ;
- print "rus:$rus, counter:$counter, array:@array \n" ;
- >
На п'ятий запуск скрипт поверне відповідь:
- # Фрагмент HTTP-заголовків
- Set-Cookie: counter = 5; path=/; expires=Wed, 05-Mar-2014 17:39:10 GMT; HttpOnly
- Set-Cookie: rus=%D0%A3%D1%80%D0%B0%21; path=/
- Set-Cookie: array=a&b2%3&0&1&2&3&4; path=/
- # Тіло відповіді
- Cookie HTTP header:counter = 4; rus=%D0%A3%D1%80%D0%B0%21; array=a&bpb%26c&0&1&2&3;
- rus:Ура!, counter:4, array:a b&c 0 1 2 3
За повним описом функціональності модуля звертайтеся до документації та вихідного коду.
Робота з cookie на PHP
PHP також надає стандартний інтерфейс для роботи з cookies, хоч і менш зручний, ніж у Perl. Для читання вхідних cookies на ім'я використовується асоціативний масив $_COOKIE. Для встановлення - функція setcookie з аргументами: ім'я, значення, атрибути expires, path, domain, secure, httponly. Усі аргументи, крім імені, є опціональними.
Зверніть увагу, що 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-скрипту, за винятком відмінностей у вигляді серіалізованого масиву.
Специфікація DOM Level 2 HTML визначає атрибут document.cookie для доступу до cookies на стороні клієнта. Механізм роботи з ним дуже простий і схожий на роботу з HTTP-заголовками на стороні сервера: для отримання заголовка Cookie потрібно прочитати значення атрибута, а для встановлення заголовка Set-Cookie привласнити його значення атрибуту.
- // Встановлення значень
- document. cookie = 'aaa=111;' ;
- document. cookie = 'bbb=222;' ;
- // Виведеaaa=111; bbb = 222
- console. log (document. cookie);
- // Видалення значень
- document. cookie = 'aaa=; max-age=0; ;
- document.cookie = bbb =; max-age=0; ;
- /* Не встановлюйте cookies з однаковими іменами,
- але різними атрибутами domain та path */
- document. cookie = 'ccc=ccc; domain=falstart.com;' ;
- document. cookie = 'ccc=bbb; domain=subdomain.falstart.com;' ;
- document. cookie = 'ccc=; max-age=0; ;
- /* Висновок залежить від браузера:
- може вивестиccc=bbb; ccc=ccc абоccc=ccc */
- console. log (document. cookie);
Цей механізм є низькорівневим, як і пряма робота з заголовками HTTP на стороні сервера. Використовувати його безпосередньо не рекомендується з тих самих причин — через трудомісткість розбору та формування рядків з урахуванням граничних випадків: форматування дати та часу, екранування спецсимволів, представлення символів національних алфавітів. Рекомендується обернути роботу з атрибутом до бібліотеки, що надає розробнику більш зручний інтерфейс. Один із варіантів реалізації розглянуто далі.