cookie的安全性

cookie是跟用户隐私、web安全有非常大的关联。 在必要的时候我们要使用cookie来完成开发,但是也要充分考虑在使用cookie时的安全性问题。

目前与cookie安全性`相关的flag如下图:

分别是:

  • domain
  • path
  • expires
  • httpOnly
  • secure
  • sameSite
    设置cookie有两种方式,一种是通过document.cookie这个前端api,第二种是通过httpSet-Cookie这个响应头。

domain

这个flag控制cookie的共享范围的要素之一。不管使用哪个方式设置cookie,都应该在cookie设置后,去看看cookie设置之后的domain值。

http://liuyunzhuge.com:5500/client/html/demo1.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<title></title>
</head>

<body>
<script>
document.cookie = 'test=foo'
</script>
</body>

</html>

上面这份代码,会添加一个cookie

它的domainliuyunzhuge.com。这个是一个不带.作为前缀的domain,所以这样的cookie仅限制在liuyunzhuge.com这个域下的页面可以访问。

换一下设置方式:

http://liuyunzhuge.com:5500/client/html/demo2.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<title></title>
</head>

<body>
<script>
document.cookie = 'test=foo; domain=liuyunzhuge.com;'
</script>
</body>

</html>
</html>

这种方式设置的cookiedomain会自动添加.前缀:

.前缀的domain,相应的cookiepath这个flag也满足的前提下,可以共享给子域。比如上方的cookie在访问http://a.liuyunzhuge.com:5500/client/html/demo.html的时候,也能访问到。

domain这个flag一定要注意它有没有.前缀。如果你的业务,需要把cookie共享给子域,那么.就是必须的,如果你的业务不需要共享cookie给子域,那一定不要给cookie的domain设置.前缀。

path

path是控制cookie作用范围的另一个要素。 默认情况下,cookie被设置到当前文档所对应的目录:
比如上方的http://liuyunzhuge.com:5500/client/html/demo2.html,会把cookie设置到/client/html这个path只有满足path目录以及path子目录的页面,才能访问path对应的cookie。比如/client/pages/demo.html这个页面,就无法访问到path: /client/html这个cookie,但是/client/html/demo3.html以及/client/html/sub/demo.html是可以的。

document.cookie也可以手动设置path

http://liuyunzhuge.com:5500/client/html/demo3.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<title></title>
</head>

<body>
<script>
document.cookie = 'test=foo; domain=liuyunzhuge.com; path=/'
</script>
</body>

</html>
</html>

虽然path设置为/很方便,同域的所有页面都能访问它,但是也增加了cookie的作用范围,从而增加了额外的安全风险,所以在实际设置path的时候,要根据它的真实业务范围来设定。

expires

expires设置cookie的生存时间。 默认情况下,如果不主动设置expires,那么cookie的生存周期就是session级别的,与浏览器窗口的会话保持一直,当浏览器窗口关闭以后,这样的cookie就会被自动清理。

可以通过max-ageexpires两种方式来明确指定cookie的生存时间:

  • max-age 指定一个整数,代表cookie可以存活的秒数。 浏览器根据当前时间和max-age来设定cookieexpires时间。所以max-age是比较好使用的。
  • expires 指定一个GMTString来代表cookie的准确失效时间,js中通过new Date().toUTCString()可以把一个日期对象转换为GMTString

示例:

http://liuyunzhuge.com:5500/client/html/demo4.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<title></title>
</head>

<body>
<script>
document.cookie = 'test=foo; domain=liuyunzhuge.com; path=/; max-age=100'
</script>
</body>

</html>
</html>

expires设置的越长,则越有被窃取和泄露的风险,所以cookieexpires也要合理设置。

httpOnly

httpOnly这个flag决定了cookie不能被document.cookie所访问,只能由http来读写。 这对于保护cookie不被XSS攻击所窃取是很有用的,所以那些跟用户相关的很重要的cookie一定要考虑是否添加httpOnly的标记。

这个flag也无法通过document.cookie来设置。

secure

secure这个flag标识了cookie只能在https中使用,http站点完全无法使用secure这个flag

sameSite

这是一个新的flag,较新的浏览器都已支持。它的作用是允许cookie在跨站请求时不会被发送,从而阻止CSRF攻击。假如你访问了某个存在CSRF漏洞的一个网站A,并且这个网站的身份验证是基于cookie的,接着你没有关闭浏览器,访问到了一个恶意网站B,它的页面里面有一个利用A网站CSRF漏洞的请求;默认情况下,浏览器并不知道这个请求是恶意的,所以它会把这个请求按正常方式处理,比如携带你访问A时的cookie,这样就导致CSRF攻击成功。 sameSite就是告诉浏览器有些cookie在进行跨站请求时不要发送,从而去避免CSRF攻击。

它有3个值:

  • none 这个值就是禁用sameSite的意思;
  • strict 这个值就是完全禁止跨站携带cookie的意思;
  • lax 在新版浏览器中,这个将成为默认选项。 这个值也有控制cookie不要跨站发送的作用,但它不像strict是严格控制的,这个值会允许部分跨站请求携带cookie。比如链接跳转或iframe加载。

lax是合理的。如果A站页面内有一个可跳转至B站页面的链接,如果点击这个链接,不能把B站页面的相关cookie带过去,那么到了B站必然有些状态要重置。 典型的就是我博客内的这个github链接,如果点击跳转到github不能携带githubcookie,那么每次到了github就都要重新登录。

https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Cookies