删除服务器端Cookie的正确方法


141

在我的身份验证过程中,我会在用户登录时创建一个唯一令牌,并将其放入用于身份验证的Cookie中。

所以我会从服务器发送这样的消息:

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

适用于所有浏览器。然后要删除一个cookie,我发送一个类似的cookie,其expires字段设置为1970年1月1日

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

而且在Firefox上运行良好,但不会在IE或Safari上删除Cookie。

那么删除cookie的最佳方法是什么(最好不使用JavaScript)?过去设置过期方法似乎很庞大。还有为什么这在FF中有效而在IE或Safari中无效?


Answers:


208

发送带有附加值的相同cookie值; expires不会破坏该cookie。

通过设置一个空值使cookie无效,并包括一个expires字段:

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

请注意,您不能强制所有浏览器删除Cookie。客户端可以以这样的方式配置浏览器:即使cookie过期,cookie也可以保留。如上所述设置值将解决此问题。


52
我建议使用空文本代替垃圾,"deleted"以免日后引起混淆,其潜在的合法价值等于“已删除”
yegor256

8
@raulk是的,您是正确的。有趣的是,它以前没有被注意到,希望它不会引起太多问题。yegor256,在大多数情况下,应该使用空值。相关:有些人可能想知道为什么即使发送此标头后也不会删除其cookie。在这种情况下,请查看其他域的cookie。例如,删除后foo=bar; domain=www.example.comfoo=qux; domain=.example.com将使用另一个cookie 。
Lekensteyn 2013年

3
“客户端可以以这样的方式配置浏览器:即使cookie过期,cookie也可以保留。如上所述设置值将解决此问题。” 客户端是否也可以将浏览器配置为忽略您的请求,即也将Cookie内容设置为“已删除”?您没有办法强迫客户做任何不想做的事情。
Ajedi32

@ Ajedi32可以,但是然后(您作为客户端),您必须付出额外的努力。忽略空值的行为更为常见,对于浏览器而言,忽略此类请求(尤其是对于无效的会话ID)尤其无济于事。
Lekensteyn

2
-1是因为我从未见过将浏览器配置为忽略Cookie过期的方法,并且不相信存在提供这种选项的任何浏览器。而且,在@DaveJarvis进行了相当大胆的编辑之后,答案的第一句话现在对于任何主流浏览器或任何符合规范的用户代理都是完全错误的。tools.ietf.org/search/rfc6265#section-5.3规定:“如果任何时候在cookie存储中存在过期的cookie,则用户代理必须从cookie存储中逐出所有过期的cookie。” 据我所知,这实际上是每个浏览器的功能。
Mark Amery

46

在我撰写此答案时,该问题可接受答案似乎表明,浏览器在收到其Expires值在过去的替换cookie时,不需要删除cookie 。该说法是错误的。设置Expires为过去是删除Cookie的标准且符合规范的方式,规范要求用​​户代理遵守该规范。

Expires过去使用属性删除Cookie是正确的,并且是删除规范规定的Cookie的方法。RFC 6255的示例部分指出:

最后,要删除cookie,服务器将返回一个Set-Cookie标头,其中包含过去的到期日期。仅当Set-Cookie标头中的Path和Domain属性与创建cookie时使用的值匹配时,服务器才能成功删除cookie。

用户代理要求”部分包括以下要求,这些要求合在一起,即如果用户代理收到其过期日期为过去的同名新Cookie,则必须立即删除该Cookie。

  1. 如果[当接收到一个新的cookie时] cookie存储区包含一个与新创建的cookie具有相同名称,域和路径的cookie:

    1. ...
    2. ...
    3. 更新新创建的cookie的创建时间以匹配旧cookie的创建时间。
    4. 从cookie存储区中删除旧cookie。
  2. 将新创建的cookie插入cookie存储。

如果cookie具有过去的过期日期,则cookie被“过期”。

如果任何时候在cookie存储中存在过期的cookie,则用户代理必须从cookie存储中逐出所有过期的cookie。

上面的点11-3、11-4和12一起表示,当接收到具有相同名称,域和路径的新cookie时,必须删除旧的cookie并将其替换为新的cookie。最后,关于过期cookie的以下几点进一步表明,完成此操作后,必须立即将 cookie 逐出。在这一点上,规范没有为浏览器提供回旋余地。如果浏览器向用户提供禁用cookie过期的选项(如已接受的答案表明某些浏览器会这样做),则将违反规范。(这种功能也几乎没有用,据我所知它在任何浏览器中都不存在。)

那么,为什么这个问题的OP观察到这种方法失败了?尽管我尚未清除Internet Explorer的副本来检查其行为,但我怀疑这是因为OP的Expires值格式不正确!他们使用了这个值:

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;

那么大概就可以了。


15

将“过期”设置为过去的日期是删除Cookie的标准方法。

您的问题可能是因为日期格式不是常规格式。IE可能只期望GMT。


2

使用Max-Age = -1而不是“ Expires”。它更短,对语法的选择更少,而且无论如何,Max-Age都优先于Expires。


-1

对于GlassFish Jersey JAX-RS实现,我已经通过描述所有通用参数的通用方法解决了该问题。至少三个参数必须相等:name(=“ name”),path(=“ /”)和domain(= 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();

对我来说,当我将maxAge设置为0时,它将输出一个Max-Age = 0的cookie,Chrome似乎忽略了它。在RFC 6265第4.1.1节中,将Max-Age的语法指定为“非零数字”。那可能就是原因。尽管,如@ JoshC13所述,第5.2.2节确实讨论了解释小于或等于零的值。所以那里确实有点矛盾……
Matthijs Wessels

我不知道详细信息,但是这些值配对确实可以在Chrome和其他浏览器中使用:maxAgeInMinutes * 60,到期。
RoutesMaps.com

1
@MatthijsWessels好抓住!我更深入地研究了一下,实际上明显的矛盾是故意的,正如勘误表中的rfc-editor.org/errata/eid3430所示。为了“最大程度地提高互操作性”,要求用户代理将零或负数解释Max-Age为最早的可表示日期和时间,但是禁止服务器发送这样的Max-Age值。我想作者Max-Age=0在编写规范时就已经知道了现有的无法处理的客户端和发送该客户端的服务器,并试图从两端缓解该问题。
马克·阿默里'18

@ Crimean.us我也无法复制。也许我做错了
Matthijs Wessels '18

@MatthijsWessels在我的示例中,通过将到期日期设置为ZonedDateTime.now()。plusMinutes(maxAgeInMinutes),解决了忽略Max-Age = 0的问题。对于maxAgeInMinutes = 0,它是当前日期时间。此代码在实际的Web应用程序中已使用了很长时间。
RoutesMaps.com
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.