Cookie 与 CSRF 攻击:如何使用 SameSite 属性防御攻击
Cookie 与 CSRF 攻击:如何使用 SameSite 属性防御攻击
HTTP Set-Cookie 编程概述
HTTP Set-Cookie 响应头用于将 cookie 从服务器发送到用户代理,以便用户代理稍后将其发送回服务器。这可以用于跟踪用户会话、记住用户偏好或存储其他与用户相关的信息。
工作原理
当用户访问网站时,服务器可以向用户发送一个或多个 Set-Cookie 响应头。浏览器通常会存储 cookie 并将其与发送到同一服务器的请求一起发送在 Cookie HTTP 头中。
主要属性
- Name: cookie 的名称。
- Value: cookie 的值。
- Expires: cookie 的到期时间。
- Domain: cookie 所属的域。
- Path: cookie 所属的路径。
- Secure: cookie 是否仅在 HTTPS 连接上发送。
- HttpOnly: cookie 是否仅可由服务器端脚本访问。
- SameSite: cookie 是否在跨站点请求中发送。
示例代码
# 使用 Flask 框架设置 cookie
from flask import Flask
app = Flask(__name__)
@app.route("/")
def index():
# 设置一个名为 "session_id" 的 cookie
response = make_response("Hello, world!")
response.set_cookie("session_id", "1234567890")
return response
if __name__ == "__main__":
app.run()
注释
- cookie 的名称和值必须是有效的 ASCII 字符。
- cookie 的到期时间可以是绝对时间或相对时间。
- cookie 的域和路径必须是有效的 URL。
- Secure 和 HttpOnly 属性可以提高安全性。
- SameSite 属性可以防止 CSRF 攻击。
错误
- 如果 cookie 名称或值无效,则会引发错误。
- 如果 cookie 的到期时间无效,则会引发错误。
- 如果 cookie 的域或路径无效,则会引发错误。
中文资源
- 阮一峰的网络日志 - HTTP 协议中的 Cookie: [移除了无效网址]
- 菜鸟教程 - HTTP Cookie: [移除了无效网址]
总结
HTTP Set-Cookie 响应头是 HTTP 协议中一个重要的部分,用于在服务器和用户代理之间存储信息。它可以用于各种目的,例如跟踪用户会话、记住用户偏好或存储其他与用户相关的信息。
Set-Cookie
Set-Cookie
HTTP 响应头用于将 cookie 从服务器发送到用户代理,以便用户代理稍后可以将其发送回服务器。要发送多个 cookie,应在同一响应中发送多个 Set-Cookie
标头。
警告:根据 Fetch 规范的要求,浏览器会阻止前端 JavaScript 代码访问 Set-Cookie
标头,该规范将 Set-Cookie
定义为 forbidden response-header name ,而 must be filtered out 来自暴露给前端代码的任何响应。
有关更多信息,请参阅 Using HTTP cookies 指南。
Header type | Response header |
---|---|
禁止的标头名称 | no |
禁止的响应标头名称 | yes |
Syntax
Set-Cookie: <cookie-name>=<cookie-value>
Set-Cookie: <cookie-name>=<cookie-value>; Expires=<date>
Set-Cookie: <cookie-name>=<cookie-value>; Max-Age=<number>
Set-Cookie: <cookie-name>=<cookie-value>; Domain=<domain-value>
Set-Cookie: <cookie-name>=<cookie-value>; Path=<path-value>
Set-Cookie: <cookie-name>=<cookie-value>; Secure
Set-Cookie: <cookie-name>=<cookie-value>; HttpOnly
Set-Cookie: <cookie-name>=<cookie-value>; SameSite=Strict
Set-Cookie: <cookie-name>=<cookie-value>; SameSite=Lax
Set-Cookie: <cookie-name>=<cookie-value>; SameSite=None; Secure
// Multiple attributes are also possible, for example:
Set-Cookie: <cookie-name>=<cookie-value>; Domain=<domain-value>; Secure; HttpOnly
Attributes
-
<cookie-name>=<cookie-value>
定义 cookie 名称及其值。Cookie 定义以名称-值对开始。
<cookie-name>
可以包含任何 US-ASCII 字符,但以下字符除外:控制字符、空格或制表符。它还不得包含如下分隔符:( ) < > @ , ; : \ " / [ ] ? = { }
。<cookie-value>
可以选择用双引号括起来,并包含除控制字符、 Whitespace 、双引号、逗号、分号和反斜杠之外的任何 US-ASCII 字符。 编码:许多实现对 cookie 值执行 URL 编码。然而,RFC 规范并不要求这样做。URL 编码确实有助于满足<cookie-value>
允许的字符要求。 注意:有些<cookie-name>
有特定的语义:__Secure-
:名称以__Secure-
开头的 Cookie(破折号是前缀的一部分)必须使用安全页面 (HTTPS) 中的secure
标志进行设置。__Host-
:名称以__Host-
开头的 Cookie 必须使用secure
标志进行设置,必须来自安全页面 (HTTPS),不得指定域(因此不会发送到子域),并且路径必须为/
。 -
Expires=<date>
Optional将 cookie 的最大生命周期指示为 HTTP 日期时间戳。请参阅
Date
了解所需的格式。 如果未指定,该 cookie 将成为会话 cookie。当客户端关闭时,会话结束,之后会话 cookie 将被删除。 警告:许多网络浏览器都有会话恢复功能,可以保存所有选项卡并在下次使用浏览器时恢复它们。会话 cookie 也将被恢复,就好像浏览器从未关闭过一样。 设置Expires
日期时,截止日期与设置 cookie 的客户端相关,而不是与服务器相关。 -
Max-Age=<number>
Optional表示 cookie 过期之前的秒数。零或负数将使 cookie 立即过期。如果同时设置了
Expires
和Max-Age
,则Max-Age
优先。 -
Domain=<domain-value>
Optional定义 cookie 将发送到的主机。 如果省略,此属性默认为当前文档 URL 的主机,不包括子域。 与早期规范相反,域名 (
.example.com
) 中的前导点将被忽略。 不允许使用多个主机/域值,但如果指定了域,则始终包含子域。 -
Path=<path-value>
Optional指示浏览器发送
Cookie
标头所请求的 URL 中必须存在的路径。 正斜杠 (/
) 字符被解释为目录分隔符,并且子目录也会匹配。例如,对于Path=/docs
, 请求路径/docs
、/docs/
、/docs/Web/
和/docs/Web/HTTP
将全部匹配。 请求路径/
、/docsets
、/fr/docs
将不匹配。 -
Secure
Optional表示仅当使用
https:
方案发出请求时(本地主机除外)才将 cookie 发送到服务器,因此更能抵抗 man-in-the-middle 攻击。 注意:不要假设Secure
阻止对 cookie 中的敏感信息(会话密钥、登录详细信息等)的所有访问。如果未设置HttpOnly
cookie 属性,则仍然可以通过访问客户端硬盘或从 JavaScript 读取/修改具有此属性的 Cookie。 不安全站点 (http:
) 无法设置具有Secure
属性的 cookie(自 Chrome 52 和 Firefox 52 起)。对于 Firefox,当 localhost 设置Secure
属性时,https:
要求将被忽略(自 Firefox 75 起)。 -
HttpOnly
Optional禁止 JavaScript 访问 cookie,例如通过
Document.cookie
属性。请注意,使用HttpOnly
创建的 cookie 仍将随 JavaScript 发起的请求一起发送,例如,在调用XMLHttpRequest.send()
或fetch()
时。这可以减轻针对跨站点脚本( XSS )的攻击。 -
SameSite=<samesite-value>
Optional控制是否随跨站点请求发送 cookie,从而提供一些针对跨站点请求伪造攻击的保护 ( CSRF )。 可能的属性值为:
Strict
表示浏览器仅针对同站点请求发送 Cookie,即来自设置 Cookie 的同一站点的请求。如果请求源自不同的域或方案(即使具有相同的域),则不会发送具有SameSite=Strict
属性的 cookie。Lax
意味着 Cookie 不会在跨站点请求(例如加载图像或框架的请求)上发送,而是在用户从外部站点导航到源站点(例如,点击链接时)时发送。如果未指定SameSite
属性,这是默认行为。None
意味着浏览器会通过跨站点请求和同站点请求发送 cookie。设置此值时还必须设置Secure
属性,例如SameSite=None; Secure
注意:与 SameSite Cookies 相关的标准最近发生了变化,例如: 如果未指定SameSite
,则 cookie 发送行为为SameSite=Lax
。以前,默认情况下会为所有请求发送 cookie。 具有SameSite=None
的 Cookie 现在还必须指定Secure
属性(换句话说,它们需要安全上下文)。 如果使用不同的方案(http:
或https:
)发送,来自同一域的 Cookie 将不再被视为来自同一站点。 有关特定浏览器实现的信息,请参阅 Browser compatibility 表(行: "SameSite
: Defaults toLax
" 、 "SameSite
: Secure context required" 和 "SameSite
: URL scheme-aware (" schemeful ")" )。
Examples
Session cookie
当客户端关闭时,会话 cookie 将被删除。如果 Cookie 未指定 Expires
或 Max-Age
属性,则它们是会话 Cookie。
Set-Cookie: sessionId=38afes7a8
Permanent cookie
永久 cookie 将在特定日期 ( Expires
) 或特定时间长度 ( Max-Age
) 后删除,而不是在客户端关闭时删除。
Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT
Set-Cookie: id=a3fWa; Max-Age=2592000
Invalid domains
不包括设置它的服务器的域的 cookie should be rejected by the user agent 。
如果由 originalcompany.com
上托管的服务器设置以下 cookie 将被拒绝:
Set-Cookie: qwerty=219ffwef9w0f; Domain=somecompany.co.uk
服务域的子域的 cookie 将被拒绝。
如果由 example.com
上托管的服务器设置以下 cookie 将被拒绝:
Set-Cookie: sessionId=e8bb43229de9; Domain=foo.example.com
Cookie prefixes
仅当使用来自安全 (HTTPS) 源的 secure
属性设置了前缀为 __Secure-
或 __Host-
的 Cookie 名称时,才可以使用它们。
此外,带有 __Host-
前缀的 cookie 必须具有 /
路径(表示主机上的任何路径),并且不得具有 Domain
属性。
警告:对于不实现 cookie 前缀的客户端,您不能指望这些额外的保证,并且带前缀的 cookie 将始终被接受。
// 来自安全来源 (HTTPS) 时两者均被接受
Set-Cookie: __Secure-ID=123; Secure; Domain=example.com
Set-Cookie: __Host-ID=123; Secure; Path=/
// 由于缺少安全属性而被拒绝
Set-Cookie: __Secure-id=1
// 由于缺少 Path=/ 属性而被拒绝
Set-Cookie: __Host-id=1; Secure
// 由于设置域而被拒绝
Set-Cookie: __Host-id=1; Secure; Path=/; Domain=example.com
Specifications
Specification |
---|
HTTP 状态管理机制 # sane-set-cookie |
Browser compatibility
Desktop | Mobile | |||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|
Chrome | Edge | Firefox | Internet Explorer | Opera | Safari | WebView Android | Chrome Android | 安卓版火狐浏览器 | Opera Android | iOS 上的 Safari | Samsung Internet | |
HttpOnly |
1 | 12 | 3 | 9 | 11 | 5 | 37 | Yes | 4 | Yes | 4 | Yes |
Max-Age |
Yes | 12 | Yes | 8 | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes |
SameSite |
51 | 16 | 60 | 11 | 39 | 13 macOS 10.14 (Mojave) 上的 Safari 13 将 SameSite=None 和无效值视为 Strict 。此问题已在版本 10.15 (Catalina) 及更高版本中修复。 12 在 10.15 Catalina 之前的 macOS 中,将 SameSite=None 和无效值视为 Strict 。参见 bug 198181 。 |
51 | 51 | 60 | 41 | 13 12.2 在 13 之前的 iOS 中,将 SameSite=None 和无效值视为 Strict 。请参阅 bug 198181 。 |
5.0 |
Set-Cookie |
Yes | 12 | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes |
cookie_prefixes |
49 | 79 | 50 | No | 36 | Yes | 49 | 49 | 50 | 36 | Yes | 5.0 |
Compatibility notes
- 从 Chrome 52 和 Firefox 52 开始,不安全站点 (
http:
) 无法再设置具有Secure
属性的 cookie。
See also
Licensed under the Creative Commons Attribution-ShareAlike License v2.5 or later.
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie