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

153

Для процесса аутентификации я создаю уникальный токен, когда пользователь входит в систему, и помещаю его в файл 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

Ответы:

223

Отправка того же значения cookie с ; expiresдобавлением не приведет к его уничтожению.

Сделайте файл cookie недействительным, установив пустое значение и также включив expiresполе:

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

Обратите внимание, что вы не можете заставить все браузеры удалить cookie. Клиент может настроить браузер таким образом, чтобы файл cookie сохранялся, даже если срок его действия истек. Установка значения, как описано выше, решит эту проблему.

Lekensteyn
источник
54
Я бы порекомендовал использовать пустой текст как мусор вместо того "deleted", чтобы избежать путаницы позже с потенциально допустимым значением, равным «удалено»
yegor256
8
@raulk Да, вы правы. Забавно, что это не было замечено раньше, надеюсь, это не вызвало особых проблем. yegor256, в большинстве случаев должно работать пустое значение. По теме: некоторые люди могут задаться вопросом, почему их файлы cookie не удаляются даже после отправки этого заголовка. В этом случае обратите внимание на файлы cookie из других доменов. Например, после удаления будет использоваться foo=bar; domain=www.example.comдругой файл cookie foo=qux; domain=.example.com.
Lekensteyn
3
«Клиент может настроить браузер таким образом, чтобы файл cookie сохранялся, даже если срок его действия истек. Установка значения, как описано выше, решит эту проблему». Не мог ли клиент настроить браузер так, чтобы он игнорировал ваш запрос, чтобы также установить для содержимого cookie значение «удалено»? У вас нет возможности заставить клиента делать то, чего он не хочет.
Ajedi32
@ Ajedi32 Можно, но для этого вам придется приложить дополнительные усилия (как клиент). Поведение игнорирования пустого значения является гораздо более распространенным, браузеру не имеет смысла игнорировать такие запросы, особенно для идентификаторов сеанса, которые недействительны.
Lekensteyn
2
-1, потому что я никогда не видел способа настроить браузер так, чтобы он игнорировал истечение срока действия файлов cookie, и не уверен, что существует какой-либо браузер, предлагающий такую ​​возможность. Более того, первое предложение вашего ответа после довольно смелого редактирования @DaveJarvis теперь полностью ложно для любого основного браузера или любого пользовательского агента, соответствующего спецификации. tools.ietf.org/search/rfc6265#section-5.3 диктует, что «Пользовательский агент ДОЛЖЕН удалить все просроченные куки из хранилища куки, если в любое время просроченный куки существует в хранилище куки». и, насколько мне известно, именно это и делает каждый браузер.
Марк Эмери
55

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

Использование Expiresатрибута в прошлом для удаления файла cookie является правильным и является способом удаления файлов cookie, указанным в спецификации. В разделе примеров RFC 6255 говорится:

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

Раздел Требования к пользовательскому агенту включает следующие требования, которые вместе приводят к тому, что файл cookie должен быть немедленно удален, если пользовательский агент получает новый файл cookie с тем же именем, срок действия которого уже прошел.

  1. Если [при получении нового файла cookie] хранилище файлов cookie содержит файл cookie с тем же именем, доменом и путем, что и новый файл cookie:

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

Файл cookie считается «просроченным», если срок действия cookie истек в прошлом.

Пользовательский агент ДОЛЖЕН удалить все просроченные куки из хранилища куки, если в любое время просроченный куки существует в хранилище куки.

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

Почему же тогда ОП этого вопроса обнаружил, что этот подход терпит неудачу? Хотя я не очищал копию Internet Explorer от пыли, чтобы проверить его поведение, я подозреваю, что это произошло из-за Expiresнеправильного формата значения OP ! Они использовали это значение:

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

Однако это синтаксически неверно по двум причинам.

Раздел синтаксиса спецификации диктует, что значение Expiresатрибута должно быть

rfc1123 -date, определенный в [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;

тогда это предположительно сработало бы.

Марк Эмери
источник
2
Более чистый способ сделать это - создать такую ​​строку. Например в JS:new Date(0).toUTCString()
Херобрин
15

Установка истечения срока действия на прошедшую дату является стандартным способом удаления файла cookie.

Вероятно, ваша проблема связана с нестандартным форматом даты. IE, вероятно, ожидает только GMT.

бесспорный
источник
3

Используйте Max-Age = -1 вместо «Expires». Он короче, менее требователен к синтаксису, и Max-Age в любом случае имеет приоритет над Expires.

Стивен Пембертон
источник
-1

Для реализации JAX-RS в GlassFish Jersey я решил эту проблему обычным методом, описав все общие параметры. По крайней мере три параметра должны быть равны: имя (= "name"), путь (= "/") и домен (= null):

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();
RoutesMaps.com
источник
для меня, когда я устанавливаю 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 Хороший улов! Я копнул немного глубже, и очевидное противоречие на самом деле намеренно, как отмечено в опечатках на 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