Правильний спосіб видалення файлів cookie на стороні сервера


141

Для свого процесу аутентифікації я створюю унікальний маркер, коли користувач входить у систему та вкладає його у файл cookie, який використовується для аутентифікації.

Тож я б надіслав щось подібне з сервера:

Set-Cookie: token=$2a$12$T94df7ArHkpkX7RGYndcq.fKU.oRlkVLOkCBNrMilaSWnTcWtCfJC; path=/;

Що працює у всіх браузерах. Потім для видалення файлу cookie я надсилаю подібний файл cookie з expiresполем, встановленим на 1 січня 1970 року

Set-Cookie: token=$2a$12$T94df7ArHkpkX7RGYndcq.fKU.oRlkVLOkCBNrMilaSWnTcWtCfJC; path=/; expires=Thu, Jan 01 1970 00:00:00 UTC; 

І це добре працює на Firefox, але не видаляє файли cookie в IE або Safari.

Отже, який найкращий спосіб видалити cookie (бажано без JavaScript)? Метод «встановити-закінчується» в минулому здається громіздким. А також чому це працює у FF, але не в IE чи Safari?


Дивіться також stackoverflow.com/a/20320610/212378
Alexis Wilke

Відповіді:


209

Надіслати одне і те ж значення cookie з ; expiresдоданим файлом не знищить файл cookie.

Недійсний файл cookie, встановивши порожнє значення, а також включіть expiresполе:

Set-Cookie: token=deleted; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT

Зауважте, що ви не можете змусити всі браузери видаляти файли cookie. Клієнт може налаштувати браузер таким чином, щоб файл cookie зберігався, навіть якщо термін його дії закінчився. Встановлення значення, як описано вище, вирішило б цю проблему.


52
Я б рекомендував використовувати порожній текст як сміття, а не "deleted", щоб уникнути плутанини пізніше з потенційно юридичним значенням, рівним "видаленому"
yegor256

8
@raulk Так, ти маєш рацію. Смішно, що його раніше не помічали, сподіваємось, це не спричинило занадто багато проблем. yegor256, порожнє значення має працювати в більшості випадків. Пов’язано: деякі люди можуть задатися питанням, чому їх файли cookie не видаляються навіть після надсилання цього заголовка. У такому випадку перегляньте файли cookie з інших доменів. Наприклад, після видалення буде використано foo=bar; domain=www.example.comінший файл cookie foo=qux; domain=.example.com.
Лекенштейн

3
"Клієнт може налаштувати браузер таким чином, щоб файл cookie зберігався, навіть якщо термін його дії закінчився. Встановлення значення, як описано вище, вирішило б цю проблему." Чи не міг клієнт налаштувати браузер ігнорувати ваш запит, щоб вміст файлу cookie також було "видалено"? Ви не можете змусити клієнта робити все, чого він не хоче.
Ajedi32

@ Ajedi32 Це може, але тоді для цього потрібно докласти додаткових зусиль (як клієнта). Поведінка ігнорування порожнього значення набагато частіше, браузер не має сенсу ігнорувати такі запити, особливо для ідентифікаторів сеансу, які недійсні.
Лекенштейн

2
-1 тому, що я ніколи не бачив способу налаштувати браузер на ігнорування закінчення терміну cookie, і я не переконаний у тому, що існує будь-який браузер, який пропонує такий варіант. Більше того, перше речення вашої відповіді, після досить сміливого редагування @ DaveJarvis, відтепер неправдиве для будь-якого головного веб-переглядача чи будь-якого відповідного спецслужб користувача. tools.ietf.org/search/rfc6265#section-5.3 диктує, що "Агент користувача ОБОВ'ЯЗКОВО вилучити всі збережені файли cookie з магазину файлів cookie, якщо в магазині файлів cookie в будь-який час існує cookie з минулим терміном". і наскільки мені відомо, це те, що насправді робить кожен браузер.
Марк Амері

46

На момент написання цієї відповіді у прийнятій відповіді на це запитання з'являється повідомлення про те, що веб-переглядачам не потрібно видаляти файли cookie при отриманні замінного файлу cookie, Expiresзначення якого було раніше. Це твердження хибне. НалаштуванняExpires в минулому - це стандартний, специфічно сумісний спосіб видалення файлу cookie, а користувальницькі агенти вимагають, щоб специфікація поважала його.

Використання Expiresатрибута в минулому для видалення файлу cookie є правильним і є способом видалення файлів cookie, продиктованих специфікацією. У розділі прикладів RFC 6255 зазначено:

Нарешті, щоб видалити файл cookie, сервер повертає заголовок Set-Cookie з датою закінчення терміну дії. Сервер буде успішним у видаленні файлу cookie лише у тому випадку, якщо атрибут Path та Domain у заголовку Set-Cookie збігаються зі значеннями, використаними під час створення файлу cookie.

Розділ " Вимоги до користувацького агента " включає наступні вимоги, які в сукупності означають, що файл cookie повинен бути негайно видалений, якщо користувальницький агент отримає нове печиво з тим самим іменем, термін дії якого минув раніше

  1. Якщо [при отриманні нового файлу cookie] у магазині файлів cookie міститься файл cookie з тим самим іменем, доменом і шляхом, як і нещодавно створений файл cookie:

    1. ...
    2. ...
    3. Оновіть час створення новоствореного файлу cookie, щоб він відповідав часу створення старого файлу cookie.
    4. Видаліть старий файл cookie з магазину cookie.
  2. Вставте новостворене печиво у магазин файлів cookie.

Файл cookie "закінчився", якщо у нього раніше закінчився термін придатності.

Агент користувача ОБОВ'ЯЗКОВО вилучити всі файли cookie з минулим терміном зберігання, якщо в будь-який час у магазині файлів cookie існує файл cookie, що закінчився.

Точки 11-3, 11-4 та 12 вище разом означають, що коли отримується новий файл cookie з тим самим іменем, доменом і шляхом, старий файл cookie повинен бути замінений на новий і його замінити. Нарешті, нижченаведений пункт про закінчені терміни cookie ще більше наказує, що після цього новий файл cookie також повинен бути негайно вилучений. Спеціалізація не пропонує в цьому веб-переглядачі відмінювати приміщення; якби браузер запропонував користувачеві можливість вимкнути термін дії файлів cookie, як прийнята відповідь пропонує деяким браузерам, це було б порушенням специфікації. (Така функція також мала б користь, і наскільки я знаю, її немає в жодному браузері.)

Чому тоді ОП з цього питання бачила цей підхід невдало? Хоча я і не видалив копію Internet Explorer, щоб перевірити її поведінку, я підозрюю, що це було через те, що Expiresзначення ОП було неправильним! Вони використали це значення:

expires=Thu, Jan 01 1970 00:00:00 UTC;

Однак це синтаксично недійсно двома способами.

Розділ синтаксису специфікації диктує, що значення Expiresатрибута має бути a

rfc1123-дата , визначена в [RFC2616], розділ 3.3.1

Переглядаючи друге посилання вище, ми знаходимо це, наведене як приклад формату:

Sun, 06 Nov 1994 08:49:37 GMT

і виявимо, що визначення синтаксису ...

  1. вимагає , щоб терміни записати в місяць рік форматі, а НЕ місяць день рік формат, який використовується в питання запитувача.

    Зокрема, він визначає rfc1123-dateнаступне:

    rfc1123-date = wkday "," SP date1 SP time SP "GMT"
    

    і визначає date1так:

    date1        = 2DIGIT SP month SP 4DIGIT
                 ; day month year (e.g., 02 Jun 1982)
    

і

  1. не дозволяє UTCяк часовий пояс.

    Специфікація містить таке твердження про те, які зрушення часового поясу прийнятні в такому форматі:

    Усі позначки дати та часу HTTP ОБОВ'ЯЗКОВО бути представлені у Грінвічському середньому часі (GMT), без винятку.

    Більш того , якщо ми заглибимося в оригінальній специфікації цього формату дати і часу, ми знаходимо , що у своїй первісній специфікації в https://tools.ietf.org/html/rfc822 , то Синтаксис розділу списки «UT» (тобто «універсальне час» ) як можливе значення, але не вказано не дійсним UTC (координований універсальний час). Наскільки мені відомо, використання "UTC" у такому форматі дати ніколи не було дійсним; це не було дійсним значенням, коли формат був вперше вказаний у 1982 році, і специфікація HTTP прийняла суворо більш обмежувальну версію формату, заборонивши використовувати всі значення "зони", крім "GMT".

Якщо питання Аскер тут замість цього використовував Expiresатрибут , як це , то:

expires=Thu, 01 Jan 1970 00:00:00 GMT;

то, мабуть, це спрацювало б.


15

Налаштування "закінчується" на минулу дату - це стандартний спосіб видалення файлу cookie.

Ваша проблема, ймовірно, тому, що формат дати не є звичайним. IE, ймовірно, очікує лише GMT.


2

Використовуйте Max-Age = -1, а не "Закінчується". Він коротший, менш вибагливий до синтаксису, а Max-Age у будь-якому випадку має перевагу над «Закінчується».


-1

Для реалізації GlassFish Jersey JAX-RS я вирішив цю проблему загальним методом опису всіх загальних параметрів. Принаймні три параметри повинні бути рівними: ім'я (= "ім'я"), шлях (= "/") та домен (= нуль):

public static NewCookie createDomainCookie(String value, int maxAgeInMinutes) {
    ZonedDateTime time = ZonedDateTime.now().plusMinutes(maxAgeInMinutes);
    Date expiry = time.toInstant().toEpochMilli();
    NewCookie newCookie = new NewCookie("name", value, "/", null, Cookie.DEFAULT_VERSION,null, maxAgeInMinutes*60, expiry, false, false);
    return newCookie;
}

І скористайтеся ним загальним способом встановлення файлів cookie:

NewCookie domainNewCookie = RsCookieHelper.createDomainCookie(token, 60);
Response res = Response.status(Response.Status.OK).cookie(domainNewCookie).build();

і видалити файл cookie:

NewCookie domainNewCookie = RsCookieHelper.createDomainCookie("", 0);
Response res = Response.status(Response.Status.OK).cookie(domainNewCookie).build();

для мене, коли я встановлюю maxAge на 0, він видає файл cookie з Max-Age = 0, який Chrome, здається, ігнорує. У розділі 4.1.1 RFC 6265 він визначає синтаксис Max-Age як "не нульовий разряд". Це може бути причиною. Хоча, як зазначає @ JoshC13, у розділі 5.2.2 йдеться про інтерпретацію значень, менших або рівних нулю. Тож воно там собі суперечить ...
Маттійс Весселс

Я не знаю деталей, але ці значення в парі справді працюють у Chrome та інших браузерах: maxAgeInMinutes * 60, термін дії.
RoutesMaps.com

1
@MatthijsWessels Хороший улов! Я заглибився трохи глибше, і очевидне протиріччя насправді навмисне, як зазначалося в errata на rfc-editor.org/errata/eid3430 . Для "максимальної сумісності" користувацькі агенти зобов'язані інтерпретувати нуль або мінус Max-Ageяк найдавнішу представлену дату і час, але серверам забороняється надсилати таке Max-Ageзначення. Я думаю, що автори знали як про існуючих клієнтів, які не могли обробити, так Max-Age=0і сервери, які надсилали його під час написання специфікації, і намагалися пом'якшити проблему з обох кінців.
Марк Амері

@ Crimean.us Я теж не можу спростувати. Можливо, я зробив щось не так
Маттайс Весселс

@MatthijsWessels Проблема з ігноруванням Max-Age = 0 виправлена ​​в моєму прикладі, встановивши дату закінчення терміну дії до ZonedDateTime.now (). PlusMinutes (maxAgeInMinutes). Для maxAgeInMinutes = 0 це поточний час дати. Цей код працює тривалий час у реальному веб-додатку.
RoutesMaps.com
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.