看下这个场景 – 一个用户登录了你的网站,第二天再次回来访问时...他必须再次登录。“记住我”这种功能 – 让我们关注下,我们之前都见过它 – 是指在立即使用之后把已认证状态进行持久化。这意味着他们可以关闭浏览器,关掉电脑,然后过一天或者过一周或者过一个月或者无论多久之后再回来访问时,该网站依然能识别出来他们是谁,并给他们提供和他们离开时所拥有的一模一样的功能。
我所说的就是下面这个小东西:
看起来很简单, 是吧? 也许是,但是你将会看到把它弄得一团糟的情况并不少见,而且,即使你做对了,也会有一大队人等着告诉你,实际上你仍然不够正确。让我们从一些完全错误的做法开始我们的工作。
其它翻译版本 (1) 加载中适得其反的模式
这看起来挺明显的吗,不是吗?我是说关于“记住我”这个特性的模型还是挺基础的,不是那种很容易搞错的东西。你确定吗?显然不是。
在我们讨论怎么做才能做对之前,我们先来分享几个适得其反的模式的案例。第一个案例就是Black & Decker,就是我前段时间在Security is hard, insecurity is easy里写到的那个B&D。这就是你准备登录时的样子:
这还是挺中规中矩的,有意思的东西要等你登录后才能看到。然我们来看下Cookie记录:
这是cookies的一些相关属性和值。但是高亮部分是真正值得关注的地方。假如用户没有选择"记住我 "按钮,这些cookies的信息不会存在,所以它们的功能纯粹是为了用户稍后登录。我的用户名是相当的清楚,我的密码被加过密码。注意看看这个密码,那是Base64编码!这就意味着密码被实现由Base64编码,你能拦截它在某些地方例 如 base64decode.org 这样做:
现在并非所有人知道Base64这一门加密技术 (是的,用户可以真正地这样做) 。它是相当完美合法代替数据用ASCII格式, 这个真正的故事在这里是告诉我们的密码在浏览器以文本方式。你可能会惊奇-为什么有问题呢。它是在你的浏览器,真的?黑客怎么攻击它呢?
我将要去讨论两种简单的方式。第一种方式是涉及到前面链接的不安全的信息。Black & Decker 已经暴露了ElEMH日志和这些日志相关的未处理服务器异常。我会通知它们在 50,000的北方。当ELMAH日志发生异常,那么意味着cookies(所有请求头信息)将会被记录。系统将会抛出大量的错误信息,你会接受到一个用户凭证。是的,他们应该开始恰当地保护ELMAH日志信息。但它是一个很好的例子,你怎么可以很容易地通过一个非常简单的配置撤消错误信息。
这里有另外的一个:
这就是 Aussie Farmers Direct 。它的登录界面看起来非常传统。当我们开始登录并打上"记住我"的标志的时候, 我们看一眼相关的cookies信息。
哇亲爱的,相同的处理方式但是没有任何Base64编码。事实上,这样是相当的不合理,因为你能够操作像下面这样。
XSS 你自己的 JSON cookie? 确定不! 另外的方式是改变你的密码而不要改变你的cookie。所以当你下一次返回的时候,尝试重新登入用旧的密码。哎呀。
Aussie Farmers Direct 没有暴露出ELMAH日志信息 (因为PHP友好的处理了它) 。但是,它仍然有其它的风险例如XSS(顺便说一下,它们多次负责任的关闭和处理了风险。)另外一件事情是上面2个网站的cookies处理密码没有标记作为HttpOnly, 你能看见它们在cookie列表的第二列。那就意味着客户端脚本访问这些cookies,那就意味着如果你能到网站上得到XSS。假如你能够让用户去运行XSS负载(现在有多种方式去做),你就能偷到包含的密码的cookies信息,访问Aussie Farmers 网站。缺少了HttpOnly属性是代表这两个网站上的马虎,但是核心的问题用cookies存储你的密码,然后使他们很容易通过其他疏漏。从根本上来说,最重要的原因主要是这两个网站的疏忽。他们保护用户的证书被用在网站。在网站上,用户每一次使用"记住我"特征,用户发送了一个请求。现在有一个好的选择就是发送他们的用户名和密码到他们的邮箱,或者eBay,或者它们的银行通过线路,有时候用文本,有时候访问用客户端脚本。总是放在浏览器中未受到保护。密码重用是疯狂,而这些该死的用户该付出血淋淋的责任(我在这里转述!),我们—作为开发者—当我们处理凭据时,我们必须认识到保护远远超过只是我们自己的网站。
因此,我们应该建立"记住我"的功能的真正误区。让我们开始走向正确的实践道路。
安全领域中你能经常听到的箴言之一就是“不要使用你自己的——使用已经证实是安全的”。它被非常频繁地应用于加密和身份验证方案中,但是现在我们可以将这个扩展到“记住我”的特性,以便在我们深入细节之前,先从一个好的参考实现开始。
在一个由Visual Studio 2012新建的ASP.NET MVC 4网站中,你能得到这个开箱即用的登录界面:
其它的框架有其它实现这个特性的标准模式,但这个参考起来很容易。当我们像上面这样填写字段登陆的时候(即不要让它记住我),验证成功则会返回如下cookie结果:
Set-Cookie:.ASPXAUTH=6891A5EAF17A9C35B51C4ED3C473FBA294187C97B758880F9A56E3D335E2F020B86A85E1D0074BDAB2E1C9DBE590AF67895C0F989BA137E292035A3093A702DEC9D0D8089E1D007089F75A77D1B2A79CAA800E8F62D3D807CBB86779DB52F012; path=/; HttpOnly
这只是个简单的验证cookie, 在无状态的HTTP世界里, 一个用户发送的所有请求若不是被这一小段数据关联起来的话, 都将变成完全独立的请求. 每一次这个对我唯一的cookie被发送后, 网站就会知道来访的用户是我而且我已经通过了验证. 我们可以在Chrome的Cookie记录下面看得更仔细一点儿:
顺带说一下, 第二个cookie是一个用于阻止CSRF攻击的反伪造令牌, 这和我们的验证状态没啥关系, 除此以外就没有其他cookie了.
现在, 我们登录并要求网站记住我, 下面是响应的cookie值:
Set-Cookie:.ASPXAUTH=3A92DAC7EFF4EE5B2027A13DD8ABEA8254F0A16D8059FCAF60F5533F1B7D99462DDF57320D069A493481978750526DF952D5C9EA0371C84F5CF1BFC0CCA024C2052824D4BA09670A42B85AEC7FFCB4088FC744C6C0A22749F07AF6E65E674A4A; expires=Tue, 02-Jul-2013 00:27:05 GMT; path=/; HttpOnly
啊哈 – 看到了吧?! 如果你在Chrome下面查看被分解开来的cookie值就会变得更加清楚:
现在, 我们有了一个从当前开始有效期为48小时的cookie, 相反的是不设置过期时间的cookie将会在浏览器被关闭之后被立即清除. 让我们来近距离的看一看.
纵观授权cookie的失效
事实上,这个一个可笑的安全结构和它仅仅只是前面的例子我感觉是值得写出来,但是我们在这里。在实现当中,“记住我”功能简单地归结为当授权的cookie到期。这里做的事情是控制你想让某人登入到你网站多久,它是简单的。
在这里例子当中,ASP.NET 默认使用session cookie 或者换一句说话, a cookie 没有明确的失效时间。当用户浏览器一关闭,cookie就强制到期。 这是一种方法,另外一种就是明确一个简短时间以至于浏览器是开着的,用户自动退出的经过一段时间。当然,假如系统是运行顺畅的,你也是能够控制服务器的行为,由服务器响应增加失效时间,你也能够扩展授权cookies信息的生命周期。2KB项目(www.2kb.com,源码交易平台),提供担保交易、源码交易、虚拟商品、在家创业、在线创业、任务交易、网站设计、软件设计、网络兼职、站长交易、域名交易、链接买卖、网站交易、广告买卖、站长培训、建站美工等服务