[Reprint] Even if it is dragged to the library, you can ensure that the password is not leaked

[Article from: http://blog.coderzh.com/2016/01/10/a-password-security-design-example/]

 

In the previous article " The Correct Posture for Designing a Secure Account System ", some design methods and ideas were mainly proposed, but a more specific and implementable security encryption scheme was not given. After careful thinking and understanding of some current schemes, I have designed a secure encryption scheme that I think is relatively safe. This article is mainly about this program, and readers are very welcome and looking forward to discussing it together.

 

First, let's clarify the ultimate goal of a secure encryption scheme:

Even if the data is dragged to the library, the code is leaked, and the request is hijacked, it can ensure that the user's password is not leaked.

To be more specific, our ideal absolutely safe system is probably like this:

  1. First of all, it is difficult to ensure that data is difficult to be dragged into the library.
  2. Even if the data is dragged to the library, the attacker cannot crack the user's password from it.
  3. Even if the data is dragged to the library, the attacker cannot forge the login request to pass the verification.
  4. Even if the data is dragged to the library, the attacker hijacks the user's request data, but cannot crack the user's password.

How to ensure that the data is not dragged into the database will not be discussed here. First let's talk about password encryption. Now there should be few systems that directly save the user's password, at least it will calculate the md5 of the password and save it. The irreversible encryption method of md5 is theoretically very secure, but with the emergence of rainbow tables, a large number of passwords with insufficient length can be directly deduced from the rainbow table.

 

So, md5 encryption of the password is definitely not enough. Smart programmers figured out a way, even if the user's password is very short, as long as I add a long character after his short password, and then calculate md5, it becomes very difficult to deduce the original password. The added long character is called salt, and the result encrypted in this way is called salt  加盐 Hash . for example:

salt

As we mentioned in the previous article, among the commonly used hash functions, SHA-256 and SHA-512 are more secure and harder to crack than md5. 512 instead of md5.

salt

Through the above salted hash operation, even if the attacker gets the final result, it is difficult to deduce the original password. It cannot be reversed, but it can be pushed forward. Assuming that the attacker also gets the salt value, he can enumerate and traverse all 6-digit simple passwords, add salt hash, and calculate a result comparison table, so as to crack a simple password. password. This is commonly referred to as brute force cracking.

 

To deal with brute force, I use salted slow hashes . A slow hash means that the execution of the hash function is very slow, so that it takes a very, very long time for brute force cracking to enumerate through all possible results. For example: bcrypt is such a slow hash function:

bcrypt

By adjusting  cost the parameters, you can adjust how slow the function is. Assuming that it takes 0.5 seconds for bcrypt to calculate a simple password with 6 digits, the time required is: ((26 * 2 + 10)^6) / 2 seconds, which is about 900 years.

Alright, with the above foundation in place, let's take a look at my final solution:

password_secutity

There are a lot of details in the above picture, I will break it down in stages:

1. Negotiate the key

The key agreement algorithm based on asymmetric encryption, when the communication content is completely disclosed, the two parties negotiate a key that only the two parties know, and then use the key for symmetric encryption to transmit data. Such as the ECDH key agreement used in the figure.

 

2. Request Salt

After the two parties negotiate a key SharedKey, they can use the SharedKey as the AES symmetric encryption key for communication. The client sends the server's own public key A and the encrypted user ID (uid). The server finds the Salt1 and Salt2 of the uid from the database, and then encrypts it and returns it to the client.

Note that Salt1 and Salt2 saved by the server are best stored separately from user data and stored in the database of other servers, so even if they are injected by SQL, it will be very difficult to obtain Salt1 and Salt2.

 

3. Verify password

This is the most important step. After the client gets Salt1 and Salt2, it can calculate two salted hashes:

SaltHash1 = bcrypt(SHA512(password), uid + salt1, 10)
SaltHash2 = SHA512(SaltHash1 + uid + salt2)

使用 SaltHash2 做为 AES 密钥,加密包括 uid,time,SaltHash1,RandKey 等内容传输给服务端:

Ticket = AES(SaltHash2, uid + time + SaltHash1 + RandKey)
AES(SharedKey, Ticket)

服务端使用 SharedKey 解密出 Ticket 之后,再从数据库中找到该 uid 对应的 SaltHash2 ,解密 Ticket ,得到 SaltHash1 ,使用 SaltHash1 重新计算 SaltHash2 看是否和数据库中的 SaltHash2 一致,从而验证密码是否正确。

 

校验两个哈希值是否相等时,使用时间恒定的比较函数,防止试探性攻击。

time 用于记录数据包发送的时间,用来防止录制回放攻击。

 

4. 加密传输

密码验证通过后,服务端生成一个随机的临时密钥 TempKey(使用安全的随机函数),并使用 RandKey 做为密钥,传输给客户端。之后双方的数据交互都通过 TempKey 作为 AES 密钥进行加密。

 

假设被拖库了

以上就是整个加密传输、存储的全过程。我们来假设几种攻击场景:

  1. 假设数据被拖库了,密码会泄露吗?

    数据库中的 Salt1 ,Salt2 , SaltHash2 暴露了,想从 SaltHash2 直接反解出原始密码几乎是不可能的事情。

  2. 假设数据被拖库了,攻击者能不能伪造登录请求通过验证?

    攻击者在生成 Ticket 时,需要 SaltHash1 ,但由于并不知道密码,所以无法计算出 SaltHash1 ,又无法从 SaltHash2 反推 SaltHash1 ,所以无法伪造登录请求通过验证。

  3. 假设数据被拖库了,攻击者使用中间人攻击,劫持了用户的请求,密码会被泄露吗?

    中间人拥有真实服务器所有的数据,仿冒了真实的 Server ,因此,他可以解密出 Ticket 中的 SaltHash1 ,但是 SaltHash1 是无法解密出原始密码的。所以,密码也不会被泄露。

    但是,中间人攻击可以获取到最后的 TempKey ,从而能监听后续的所有通信过程。这是很难解决的问题,因为在服务端所有东西都暴露的情况下,中间人假设可以劫持用户数据,仿冒真实 Server , 是很难和真实的 Server 区分开的。解决的方法也许只有防止被中间人攻击,保证 Server 的公钥在客户端不被篡改。

    假设攻击已经进展到了这样的程度,还有办法补救吗?有。由于攻击者只能监听用户的登录过程,并不知道真实的密码。所以,只需要在服务端对 Salt2 进行升级,即可生成新的 SaltHash2 ,从而让攻击者所有攻击失效。

    具体是这样的:用户正常的登录,服务端验证通过后,生成新的 Salt2 ,然后根据传过来的 SaltHash1 重新计算了 SaltHash2 存入数据库。下次用户再次登录时,获取到的是新的 Salt2 ,密码没有变,同样能登录,攻击者之前拖库的那份数据也失效了。

Q & A

  1. 使用 bcrypt 慢哈希函数,服务端应对大量的用户登录请求,性能承受的了吗?

    该方案中,细心一点会注意到, bcrypt 只是在客户端进行运算的,服务端是直接拿到客户端运算好的结果( SaltHash1 )后 SHA-512 计算结果进行验证的。所以,把性能压力分摊到了各个客户端。

  2. 为什么要使用两个 Salt 值?

    使用两个 Salt 值,是为了防止拖库后,劫持了用户请求后将密码破解出来。只有拥有密码的用户,才能用第一个 Salt 值计算出 SaltHash1 ,并且不能反推回原始密码。第二个 Salt 值可以加大被拖库后直接解密出 SaltHash1 的难度。

  3. 为什么要动态请求 Salt1 和 Salt2 ?

    Salt 值直接写在客户端肯定不好,而且写死了要修改还得升级客户端。动态请求 Salt 值,还可以实现不升级客户端的情况下,对密码进行动态升级:服务端可定期更换 Salt2 ,重新计算 SaltHash2 ,让攻击者即使拖了一次数据也很快处于失效状态。

  4. The database has all been towed away, what's the point of not leaking the password?

    In fact, it makes sense. Just as the remedial plan for upgrading Salt2 just mentioned, users can upgrade the account system without changing their passwords without their knowledge. At the same time, it is also a responsibility to protect the user's password and prevent it from being used by attackers to hit the library of other websites.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326274193&siteId=291194637