审查基于 RMI 的身份验证方法
Review of an authentication approach over RMI
我正在做一个项目,其中为了访问服务,客户端需要通过 RMI 验证自己(客户端有用户名和密码)。我想在不使用 SSL 的情况下实现这一点(因此,修改和嗅探是主要威胁)。
这是客户端每次访问服务时发送的 "token"。
class Token {
String username;
byte[] encryptedText;
}
Encrypted Text 变量包含密码和时间戳的组合,使用密码本身作为密钥使用 AES-256 CBC 加密。
例如:
encryptedText[0] = AES(20) //encrypt length of password
encryptedText[1] = AES(30) //encrypt length of TimeStamp
encryptedText[2-21] = AES(password) //encrypted password
encryptedText[22-51] = AES(timestamp) //encrypted timestamp
收到此令牌的服务器检索用户名的密码,并尝试使用密码作为密钥来解密加密的文本。如果结果不是密码和最近的时间戳的组合,则这是一次错误的尝试。
我知道这不是可扩展的,因为每次请求服务时,都必须获取记录,但这安全吗?
据我所知:-
- 防止嗅探(加密)
- 防止修改攻击(不会导致正确的身份验证)
- 防止重放攻击(非最近的时间戳是身份验证失败)
我假设 AES()
不仅仅意味着块密码的伪随机排列,而是具有操作模式和填充的块密码。
如果可以区分错误的填充和良好的填充,则可以通过 padding oracle attack 恢复密码。这很有可能,因为当出现 BadPaddingException 并且无法进行进一步计算时,响应可能会提前完成。
主要的两个问题是你
- 不验证密文本身并且
- 用于加密明文的密码包含在明文中(可以恢复)。
这个伪加密码比较好:
// salt and masterHash can be cached for multiple requests
salt = SecureRandom().nextBytes(8 bytes)
// adjust iterations (65336) according to appropriate performance:
masterHash = pbkdf2(password, salt, 65336)
iv = SecureRandom().nextBytes(16 bytes)
sessionKey = SecureRandom().nextBytes(16 bytes)
encKey = hmacSha256(masterHash, "enc") // crop according to intended AES key size
macKey = hmacSha256(masterHash, "mac")
ciphertext = AES(encKey, iv, sessionKey + ts.length + ts)
tag = hmacSha256(macKey, username + salt + iv + ciphertext)
return salt + iv + ciphertext + tag
此处 +
表示串联,AES
表示 CBC 模式下的 AES,使用 PKCS#7 填充。
接收方必须先导出macKey
,然后计算tag
,然后检查传输的tag
是否与计算的tag
匹配。如果是,则解密密文。如果可行,则 sessionKey
可用于进一步通信。
攻击者唯一可以做的就是通过操纵传输的请求来阻止您对服务器进行身份验证,以便在服务器上产生失败的身份验证。
我正在做一个项目,其中为了访问服务,客户端需要通过 RMI 验证自己(客户端有用户名和密码)。我想在不使用 SSL 的情况下实现这一点(因此,修改和嗅探是主要威胁)。
这是客户端每次访问服务时发送的 "token"。
class Token {
String username;
byte[] encryptedText;
}
Encrypted Text 变量包含密码和时间戳的组合,使用密码本身作为密钥使用 AES-256 CBC 加密。 例如:
encryptedText[0] = AES(20) //encrypt length of password
encryptedText[1] = AES(30) //encrypt length of TimeStamp
encryptedText[2-21] = AES(password) //encrypted password
encryptedText[22-51] = AES(timestamp) //encrypted timestamp
收到此令牌的服务器检索用户名的密码,并尝试使用密码作为密钥来解密加密的文本。如果结果不是密码和最近的时间戳的组合,则这是一次错误的尝试。
我知道这不是可扩展的,因为每次请求服务时,都必须获取记录,但这安全吗?
据我所知:-
- 防止嗅探(加密)
- 防止修改攻击(不会导致正确的身份验证)
- 防止重放攻击(非最近的时间戳是身份验证失败)
我假设 AES()
不仅仅意味着块密码的伪随机排列,而是具有操作模式和填充的块密码。
如果可以区分错误的填充和良好的填充,则可以通过 padding oracle attack 恢复密码。这很有可能,因为当出现 BadPaddingException 并且无法进行进一步计算时,响应可能会提前完成。
主要的两个问题是你
- 不验证密文本身并且
- 用于加密明文的密码包含在明文中(可以恢复)。
这个伪加密码比较好:
// salt and masterHash can be cached for multiple requests
salt = SecureRandom().nextBytes(8 bytes)
// adjust iterations (65336) according to appropriate performance:
masterHash = pbkdf2(password, salt, 65336)
iv = SecureRandom().nextBytes(16 bytes)
sessionKey = SecureRandom().nextBytes(16 bytes)
encKey = hmacSha256(masterHash, "enc") // crop according to intended AES key size
macKey = hmacSha256(masterHash, "mac")
ciphertext = AES(encKey, iv, sessionKey + ts.length + ts)
tag = hmacSha256(macKey, username + salt + iv + ciphertext)
return salt + iv + ciphertext + tag
此处 +
表示串联,AES
表示 CBC 模式下的 AES,使用 PKCS#7 填充。
接收方必须先导出macKey
,然后计算tag
,然后检查传输的tag
是否与计算的tag
匹配。如果是,则解密密文。如果可行,则 sessionKey
可用于进一步通信。
攻击者唯一可以做的就是通过操纵传输的请求来阻止您对服务器进行身份验证,以便在服务器上产生失败的身份验证。