对于web系统而言,因为HTTP协议无状态的特性,用户登陆时须要服务端生成通行证返回给浏览器。浏览器保存该通行证并在接下来的请求中携带该通行证。一般来说,web系统使用http cookie来保存和传输通行证。本文介绍http cookie的原理、特性、并分析用其保存通行证可能遇到的安全问题。javascript
本文假设使用cookie的客户端是浏览器,虽然还有其它客户端也使用cookie,但普通用户使用更多的仍是浏览器。html
一种Http状态管理机制,最先由Lou Montulli发明于1994年,最新的标准是2011年发布的RFC 6265 - HTTP State Management Mechanism。由于保存信息到客户端的本质,也有系统用cookie来缓存信息,但这并非Http Cookie的主要用途。java
Cookie既指整个状态保存机制,也指用户保存状态的数据自己,由String类型的name和value以及若干属性组成。追根溯源,其名字来源于fortune cookie,一种内含写有幸运文字小纸条的饼干。git
Cookie由服务端生成,保存在浏览器。经过两个Http Header:Set-Cookie和Cookie进行传输。Set-Cookie Header用于服务端传输登陆通行证等cookie键值对及属性给浏览器,浏览器收到并验证合法性后会作相应保存。Cookie Header用于浏览器传递cookie键值对给服务端,服务端依次来鉴别用户身份等状态信息。github
RFC6265定义了Cookie的一些属性,包含Expires、Max-Age、Domain、Path、Secure、HttpOnly。也有一些还没定义到RFC,但已经实际应用的属性,好比SameSite。Cookie写时有属性读时无属性,属性仅在设置时即Set-Cookie Header中指定,浏览器依此来决定是否接受该cookie如何使用该cookie,而一旦浏览器决定了将cookie放到Cookie Header中传递到服务端,那么浏览器不会传递更多的的信息到服务端,只会传递键值对name和value。web
不少浏览器会提供读Cookie和写Cookie的api给运行在其上Javascript脚本使用,一般的api是操做docment.cookie:算法
document.cookie=“SID=31d4d; domain=example.com; path=/;”;
Domain、Path、Name三者惟一肯定一个cookie。其它属性仅用做读写时的权限控制,不做为cookie标识。在浏览器在使用cookie时:sql
这相比浏览器同源策略更加宽松,带来不少安全问题。chrome
Domain是向上通配的:数据库
Set-Cookie: sid1=a; domain=example.com; path=/; 接受 Set-Cookie: sid2=b; domain=www.example.com; path=/; 接受 Set-Cookie: sid3=c; domain=pay.example.com; path=/; 拒绝
访问pay.example.com Cookie: sid1=a 访问www.example.com Cookie: sid1=a; sid2=b;
不少系统写cookie,习惯在域名前加一个点,写成.example.com,觉得这么写才是通配的,其实这个点是多余的,没有必要的。
Path是向下通配的:
Set‐Cookie: sid1=a; domain=example.com; path=/; Set‐Cookie: sid2=b; domain=example.com; path=/test/;
访问http://example.com/ Cookie: sid1=a; 访问http://example.com/test/ Cookie: sid1=a; sid2=b
这两个属性指定cookie有效期。不指定有效期时,cookie默认为当前session有效,当前session有效指关闭浏览器时cookie失效。
Cookie的有效期过长可能致使通行证泄露。好比用户使用了公用电脑,关闭浏览器时,没有点击注销。这样Cookie就留存在这台电脑上,其它人只需打开浏览器便可获取其在网站上的cookie。
通常保存通行证的cookie应该设置为session有效的cookie,并在用户选择记住登陆状态时,提醒用户,不要再公用电脑上作这样的勾选。
HTTP明文传输数据的特性,使得攻击者可从网路上抓包获取Cookie。
解决方案:
假如网站存在XSS漏洞,那么恶意JS可直接读取Cookie中的通行证。能够经过指定Cookie的HttpOnly属性。对于指定了HttpOnly的Cookie,浏览器会拒绝JS读写。
XSS仍有可能利用服务端漏洞获取cookie:
- 服务端有可能在请求的正常响应中包含通行证。不要笑,这真的有,尤为如今不少公司app和web共用一套后台;
- 服务端可能有漏洞致使cookie包含在请求响应中,从而被窃取。好比:Apache CEV-2012-005
因此HttpOnly不是银弹,XSS也有不少其它的危害。但至少,HttpOnly能够避免通行证在浏览器被JS直接读取。
保存了通行证的cookie由于是HttpOnly,因此XSS没法读取。但XSS仍是能够写Cookie,因为domain、path、name三者才惟一标识一个cookie,XSS能够写一个跟通行证cookie name相同,但domain或path不一样的cookie。
这样当浏览器发送请求给服务端时,服务端就会收到两个相同name的cookie。由于cookie读时无属性,因此服务端无从判断哪个才是正确的。这样会使用哪个cookie,就要看具体web server的实现,多是第一个也多是第二个。
这样经过写cookie,XSS能够轻易地使已登陆用户退登。
恶意JS可针对特定的域名和path写入一个攻击者的cookie通行证。好比针对某电商网站的余额充值domain和path,写入攻击者的通行证。该攻击者cookie在用户正常购物的请求中不会带出,当用户充值时会带出,从而使用户充值到了攻击者的帐号。
对于HTTP,公共wifi可劫持用户全部的流量,窃取cookie通行证天然不在话下。
对于HTTPS,由于能够在DNS上作手脚,能够在路由器上截取和篡改流量,在公共wifi上很容易诱导用户发出到网站的HTTP请求,几遍该网站是全站HTTPS也是如此。
所以,对于XSS写cookie的危害,公共wifi都能作到。并且能力更强,危害更大。
对于设置了secure属性的cookie,虽然不会在http请求中带出,但却可被http请求的Cookie设置覆盖。所以,设置了secure也不安全。
Although seemingly useful for protecting cookies from active network
attackers, the Secure attribute protects only the cookie's
confidentiality. An active network attacker can overwrite Secure
cookies from an insecure channel, disrupting their integrity
---- from RFC 6265
公共wifi的危害,有一个关键点,就是诱导用户对指定网站发出HTTP请求。这一点经过HSTS - HTTP Strict Transport Security能够避免。
HSTS经过设置strict‐transport‐security头,来告知浏览器,对当前域名以及全部子域名,都只使用HTTPS访问。
让浏览器对当前域名及子域名,强制进行HTTPS访问: strict‐transport‐security: max‐age=15552000;includeSubDomains;
但HSTS在现实中难以被利用。由于:
因此,真正全站部署HSTS的网站,少之又少。
o At least 4096 bytes per cookie (as measured by the sum of the
length of the cookie's name, value, and attributes).
o At least 50 cookies per domain.
o At least 3000 cookies total.
---- From RFC 6265
设置超大cookie,超多cookie,都是典型的攻击手段。上文提到的Apache CEV-2012-005漏洞,便是利用了Apache对Cookie大小的限制。
在访问恶意网站时,网站的页面代码中,可能包含到你银行帐号或是其它什么帐号的请求。因为这时浏览器会自动带出到相应域名的cookie,因此用户的登陆通行证,就很容易被恶意网站偷用。
这个问题,能够经过设置Cookie的SameSite属性来解决。设置了SameSite属性的cookie,只会在当前正在访问(浏览器地址栏)的域名与cookie的domain可匹配时,才会带出。
以下截图,是Chrome支持的cookie属性:
有人会说,cookie不就是存放在硬盘,我直接从硬盘上窃取不能够吗?
答案是能够。好比Chrome的cookie就存放在目录:C:\Users<username>\AppData\Local\Google\Chrome\User Data\Default\下,是一个sqllite的数据库文件,虽然cookie内容都进行了加密,但也是能够破解的。
只是,当你攻破一个用户的电脑,破解了chrome的加密算法,最终只是窃取到了一个用户的登陆凭证而已。这种攻击,性价比很低。
Github推出的github pages功能,一开始的时候使用的是github.com主站域名,后来由于上面提到的cookie安全问题,不得不为github pages单独启动了github.io域名。
具体可看Github的官方blog,里面提到了上面部分的cookie安全问题。blog连接:Yummy Cookies across Domains