MongoDB中使用的SCRAM-SHA1认证机制
介绍
SCRAM是密码学中的一种认证机制,全称Salted Challenge Response Authentication Mechanism。
SCRAM适用于使用基于『用户名:密码』这种简单认证模型的连接协议。
SCRAM是一个抽象的机制,在其设计中需要用到一个哈希函数,这个哈希函数是客户端和服务端协商好的,包含在具体的机制名称中。比如SCRAM-SHA1,使用SHA1作为其哈希函数。
前言
基于『用户名:密码』这种简单认证模型的协议中,客户端和服务端都知道一个用户名(username)对应一个密码(password)。在不对信道进行加密的前提下,无论是直接使用明文传输『username:password』,还是给password加个哈希的方法都low爆了,很容易被黑客攻击。一个安全性高的认证机制起码应该是具备双向认证的,即:
- 服务端需要验证连接上来的客户端知道对应的password
- 客户端需要知道这个声称是服务端的人是真正的服务端
我们来看看SCRAM是怎么做的。
初始
- 服务端使用一个salt和一个iteration-count,对password进行加盐哈希(使用H表示哈希函数,这里就是SHA1,iteration-count就是哈希迭代次数),得到一个password[s]:
password[s] = H(password, salt, iteration-count)
- 服务端拿这个password[s]分别和字符串『Client Key』和『Server Key』进行计算HMAC摘要,得到一个key[c]和一个key[s]:
key[c] = HMAC(password[s], “Client Key”)
key[s] = HMAC(password[s], “Server Key”)
- 服务端保存username、H(key[c])、key[s]、salt和iteration-count,没有保存真正的password
一次认证
- 客户端发送client-first-message给服务端,包含username和client-nonce,其中client-nonce是客户端随机生成的字符串
- 服务端返回客户端server-first-message,包含salt,iteration-count和client-nonce|server-nonce,其中server-nonce是服务端随机生成的字符串
- 客户端发送client-final-message给服务端,包含client-nonce|server-nonce和一个proof[c]。这个proof[c]就是客户端的身份证明。首先构造出这次认证的变量Auth如下:
Auth = client-first-message, server-first-message, client-final-message(without proof[c])
然后使用从服务端获取的salt和iteration-count,根据已知的password计算出加盐哈希password[s],然后根据password[s]得到key[c],再拿这个key[c]和Auth变量经过如下计算得到:
proof[c] = key[c] XOR HMAC(H(key[c]), Auth)
- 服务端使用其保存的H(key[c])和Auth计算HMAC摘要,再和proof[c]进行异或,得出key[c],再对这个key[c]进行哈希,和其保存的H(key[c])进行比较是否一致。如果一致,则客户端的认证通过,服务端接下来会构造一个proof[s]用来向客户端证明自己是服务端:
proof[s] = HMAC(key[s], Auth)
- 客户端使用password[s]得到key[s],然后使用相同算法计算key[s]和Auth的HMAC摘要,验证服务端发送过来的proof[s]是否和计算出来的一致,从而认证服务端的身份。
几个问题
Q:proof[c]为什么不能是和proof[s]一样使用HMAC(key[c], Auth)算得?
A:服务端没有保存key[c],而是保存H(key[c]),这是因为如果服务端的key[c]泄露,那么黑客可以轻易构造出proof[c],从而伪装成客户端了。
Q:那么直接使用HMAC(H(key[c]), Auth)呢?
A:同样,如果服务端的H(key[c])泄露,黑客又可以轻易构造出proof[c],从而伪装成客户端了。因此必须要加上key[c]的异或,从而证明客户端知道key[c]的值。
Q:但是如果服务端的H(Key[c])泄露,再通过网络泄露了proof[c]和Auth,就可以根据proof[c]和HMAC(H(key[c]), Auth)异或得到key[c]了?
A:是的,所以如果要求更高的安全性,还是推荐使用信道加密。
Q:nonce的作用?
A:client-nonce和server-nonce都是随机生成的字符串,这主要是为了每次认证都有个不同的Auth变量,以防止被重放攻击。
安全性分析
分析下以下几种情况下SCRAM的安全性:
- 网络流量被监听:
如果某次认证的网络流量被监听,黑客可以拿到salt、iteration-count和Auth,但是无法伪装成服务端,因为没有key[s],无法生成proof[s],完成不了最后一次认证。 - 服务端被脱裤:如果服务端被脱裤,黑客可以拿到username、H(key[c])、key[s]、salt和iteration-count,但是生成不了key[c],因此无法生成proof[c],不能伪装成客户端。
- 如果很不幸的网络流量被监听,服务端又被脱裤,那么对不起,被爆菊了,要应对这种情况,只能使用信道加密了。
参考文献
- Improved Password-Based Authentication in MongoDB 3.0: SCRAM Explained – Pt. 1
- Improved password-based authentication in MongoDB 3.0: SCRAM Explained (Part 2)
- 维基百科:Salted Challenge Response Authentication Mechanism
作者简介
郑涔,阿里巴巴资深研发工程师,主要关注分布式存储、NOSql数据库、高性能服务器等技术领域,先后参与TFS(淘宝分布式文件系统)、Tengine等项目,目前主要从事AlidCloudDB For MongoDB的研发工作,致力于让开发者用上最好的MongoDB云服务。
评论前必须登录!
注册