Cookie溢出让我们加强我们的游戏:另一种攻击将会执行,它利用RFC 6265没有明确指明一种cookie的溢出行为.大部分web服务器/接口,包括Rack,假定cookie的名字可以加密(如果他们包含不是 ASCII的字符时,这就是个疯狂的假设),所以当生成cookie列表时不会溢出: cookies = Utils.parse_query(string, ';,') { |s| Rack::Utils.unescape(s) rescue s } 这就允许一个恶意用户去设置一个cookie,这个cookie能被web框架理解成_session,尽管在浏览器里这个cookie的名字并不是_session.这个攻击会把没必要溢出的cookie字符溢出掉:GET / HTTP/1.1 Host: github.com Cookie: logged_in=yes; _session=chocolate-cookie; _%73ession=bad-cookie; { "_session" : ["chocolate-cookie", "bad-cookie"] } |
如果我们试着丢弃Rack产生的cookie列表中的第二个,我们的header就会失效.在Rack解析以后,我们会失去重要的信息:通过加密后的cookie名字和web框架接收到的名字将不一致. # This header has no effect: the cookie in # the browser is actually named `_%73ession` Set-Cookie: _session=; Expires=Thu, 01-Jan-1970 00:00:01 GMT; Path=/; Domain=.github.com; 为了解决这个问题,我们必须跳过Rack的cookie解析,可以通过禁用不溢出,然后找到所有和我们目标匹配的那些cookie名字. cookie_pairs = Rack::Utils.parse_query(cookies, ';,') { |s| s } cookie_pairs.each do |k, v| if k == '_session' && Array === v bad_cookies << k elsif k != '_session' && Rack::Utils.unescape(k) == '_session' bad_cookies << k end end
|
Cookie溢出如果你遇到了cookie的问题,我为你惋惜. 我已经有了99个cookie,我的域名不能再增加一个了. 这是一个稍微更高级的攻击,它暴露出所有web浏览器对每个域名设置的cookie的数量限制. 比如,火狐设置的数量限制是150,谷歌浏览器设置的是 180.问题是这个限制不是在每个cookie的域名属性上设置的,而是通过cookie设在的实际域名来定义的.一个单独的HTTP请求访问主域名和子 域名上的任一页面,将会发送最大数量的cookie,但哪些cookie被使用的规则确实没有定义的. |
例如谷歌浏览器不会关心父域名上的那些cookie,这些cookie是通过HTTP设置或者用Secure设置的:它将会发送180个新的cookie.这使得非常容易"剔除"每一个单独的从父域名过来的cookie,并用一些子域名上用JavaScript运行的假的cookie来替代它们: for (i = 0; i < 180; i++) { document.cookie = "cookie" + i + "=chocolate-chips; Path=/; Domain=.github.com" } 在子域名上设置了180个这样的cookie之后,所有从父域名过来的cookie就消失了.如果现在我们终止我们刚刚设置的cookie,也包含JavaScript那部分,那么子域名和父域名上的cookie列表就会变空:
for (i = 0; i < 180; i++) { document.cookie = "cookie" + i + "=chocolate-chips; Path=/; Domain=.github.com; Expires=Thu, 01-Jan-1970 00:00:01 GMT;" } /* all cookies are gone now; plant the evil one */ document.cookie = "_session=EVIL_SESSION_TOKEN; Path=/; Domain=.github.com" 这允许我们执行一个只带有一个_session的 cookie的单独的请求:这个cookie是我们用JavaScript创造的.原来的Secure和HttpOnly在_session的 cookie里没有了,并且没有方法在web服务端检测发送的cookie既不是Secure,HttpOnly,也不是在父域名中设置的,但是完全虚构 的. 在服务端只设置一个_session的cookie,就没有方法知道cookie是否被抛出了.即使我们发现了一个不合法的cookie,这样的攻击也仅能把用户从GitHub注销. |
结论正如我们看到的,通过在浏览器溢出cookie,我们可以制造带有恶意cookie的请求,这些请求不能在服务端阻隔.这里没有什么新的知识:Egnor的原始概念攻击的证据和暴露在这里的变种攻击都早已被世人所知. 现在看起来,在子域名上控制用户自定义的内容是一种安全的 自杀行为,尤其在谷歌浏览器当前实现方案下,更加剧了这种自杀行为.火狐处理父域和子域上cookie区别的方式更优雅(用更一致的排序来发送它们,并且 分离它们的存储来防止子域的溢出),谷歌浏览器就没有这种区别,并且对通过JavaScript设置的cookie和服务器通过 Secure,HttpOnly设置的cookie一视同仁,导致一个非常完美的抛出攻击. |
不管如何,通过HTTP header来传输cookie的行为是模糊的和依赖于实现的,迟早会有人提出另一种跨域抛出cookie的方式,和目标浏览器无关.
当cookie抛出的攻击并不是太危险的(比如,拦截用户session,或者实行网络欺诈/骚扰用户是不太可能的),它们会直截了当的进行,这非常使人恼火.
我们希望这篇文章能帮助大家提高这些攻击问题的防范意识和不通过迁移域名来防止这些攻击的困难点,所以迁移域名是一个激进的但最终必须的措施.