Aoang's Blog

岁月在电波中流淌,人生在音乐中升华

OWASP Top 10 - Authentication and Password Management

Aoang's Avatar 2017-03-17

很多古老的程序都存在一个普遍的问题——身份认证问题。如果有以下行为之一,就有可能存在这个问题。

1、允许弱密码(password 123456 admin)
2、使用明文、加密或弱哈希管理密码
3、允许暴力破解、自动攻击
4、使用非安全连接进行认证
5、使用 GET 请求进行认证
6、不正确的失效策略

关于身份认证可以先从客户端说起。
传统 Web 登录方式中,都会默认隐藏用户输入的密码,但是除此之外,应该默认禁用记住会话、记住密码这些功能。

用户使用的电脑并非一定是自己的、安全的,默认禁用这些功能对提升安全性的作用是很大的。

做到了这一点,应该在考虑一下后端的问题,用户的会话失效策略应该是怎么样的。在各种使用场景中,主动注销可能并不符合用户的使用习惯。

会话失效策略定义的过于严格的话,会导致用户在正常使用过程中会话失效,导致用户使用体验变差。对于这个问题,应当结合场景制定合适的会话策略。

一般情况下,必须确保用于生成会话标识符的算法足够随机,防止被爆破。

  • cookies 的过期时间应该足够合适
  • 会话标识符不会在 URL 中公开,它应该仅位于 HTTP cookie 中
  • 会话数据也必须受到保护,防止服务器的其他用户未经授权的访问
  • 如果有 HTTP 重定向到 HTTPS,那应该特别注意 MITM 攻击嗅探
  • 如果有高度敏感的操作,那么可以考虑按照请求生成 token,保持 token 足够随机,且长度足够安全

现在越来越多的网站开始使用甚至强制使用加密连接(HTTPS)了,而对于 Web 应用而言这也是必须的了。
如果数据传输不通过 TLS/SSL 进行,可能会因为中间人攻击导致用户会话的相关信息泄露,甚至服务器也会遭受入侵。

在发送认证请求的时候,尽管在 HTTPS 下,通过 GET 请求和 POST 是一样安全的,但是还是应该使用 PSOT 请求。
因为在一般的 HTTP 服务器(Nginx Apache)中,会将请求的 URL 写入日志。

在之前的文章中提到过一点,如果用户身份验证错误,程序不要返回过于详细的信息,使用“无效的用户名或密码”来代替“用户名不存在”、“密码错误”。
因为提示用户名不存在会泄露一个用户是否存在,而密码错误这个提示会暴露后端的工作模式,先验证用户名,然后比较密码。

如果可能的话,成功登录之后,应该告知用户最近一次成功、失败的访问记录,以便用户检查可疑活动。
如果对于安全性有更高的要求的话,应该采取一定的密码策略。

例如,我们应该强制用户不得使用弱密码。
可以要求用户的密码至少包括大写字母、小写字母、数字、特殊符号中的三种甚至四种,要求用户的密码长度为八位甚至十六位以上。
还可以要求用户间隔一段时间之后强制更改密码,并不得使用过去使用过的密码。

在这个方面,Microsoft 做的很强,它除了上述策略,还有黑名单策略(禁止使用网络上公布的常见的弱密码),密码不得包含用户名。
但是在提示用户安全这一块,Google 可能做的更好,更加人性化,因为上面很容易就能看到上次更改密码的时间、提示的风险等等。

不过在日志这一块上,GitHub 做的非常详细。
大家感兴趣的话,可以分别去体验一下。

除了这些密码策略,程序应该要做到防止被爆破。
比如,登录失败次数过多,程序应当限制账号登录一段时间,但是这个时间得控制好,不然的话会成为别人拒绝式攻击服务的手段之一,导致用户无止境的被冻结,无法正常使用。
还有密码应该至少存在一段时间之后才能再次更改,这样可以一定程度上的防止密码重用攻击。
对于用户重置密码时,发送重置链接、或者验证码的时候,这样的敏感信息应该具有较短的到期时间。

最后说下密码的储存策略,先说一句密码学中的格言,永远不要自己设计加密方案。

你可以自动设计加密方案,但是如果你不是这个领域内的专家,或者没有经过其他专家对方案进行分析,那你的方案可能会存在严重的安全性错误。
一个公开的加密方案,能被所有人进行审查分析,一个方案只有遭到广泛的攻击之后才是安全的。

密码学也存在很多安全性问题,对于计算机科学的安全性问题都缺乏了解,对于其他技术就更谈不上了。
在存储密码,Golang 已经有现成的哈希算法可以使用了 bcrypt scrypt PDKDF2 Argon2,而不应该使用那些不够安全的哈希算法 MD5 SHA-1

哈希和加密是两种不同的概念,拥有不同的用途。
哈希是将目标文本转换成具有相同长度的、不可逆的字符串,而加密将目标文本转换成具有不同长度的、可逆的密文。

在储存密码中,除了使用哈希,还可以使用加密,区别在哪里呢。
如果数据仅仅用于比较、验证,不需要还原成明文,那就使用哈希,反之则使用加密。
一般情况下,储存密码都会使用哈希,但是一些特殊场景下,会需要还原成明文,才需要使用加密。

一般情况下,服务器被入侵数据泄露之后,如果密码是明文,那么用户的信息就直接泄露了,但是一般情况都是使用哈希算法对密码进行处理。
起初,对于简单的密码和简单的密码系统,可以通过字典穷举进行破解,而之后出现了彩虹表,所以对于重要的场景不要使用弱哈希算法。