§加密迁移指南
从 Play 1.x 开始,Play 就附带了一个 Crypto 对象,它提供了一些加密操作。Play 内部使用它。Crypto 对象在文档中没有提到,但在 scaladoc 中被提及为“加密实用程序”。
“这些实用程序旨在作为一种便利,但重要的是要阅读每个方法的文档并了解加密背后的概念,以便正确使用此类。安全的加密很难,没有比充分了解密码学更好的替代方案。这些方法不适合所有加密需求。”
由于各种原因,提供加密实用程序作为一种便利的做法已不再可行。在 2.5.x 中,Play 特定的功能已分解为 CookieSigner
、CSRFTokenSigner
和 AESSigner
特性,并且 Crypto
单例对象已弃用。
本文档的其余部分将讨论 Crypto 背后的内部功能、加密操作的适用性(以及不适用性)以及用户从 Crypto 功能迁移的迁移路径。
有关密码学的更多信息,我们建议阅读 OWASP 密码存储备忘单。
§消息认证
Play 使用 Crypto.sign
方法为会话 cookie 提供消息认证。Crypto.sign
不应在签名 cookie 目的之外使用的原因有很多:MAC 算法独立性、附加功能以及将 HMAC 误用为密码哈希算法的可能性。
§MAC 算法独立性
Play 目前使用 HMAC-SHA1 来签署和验证会话 cookie。 HMAC 是一种加密函数,它使用密钥(定义为 play.crypto.secret 的 应用程序密钥)和消息摘要函数(在本例中为 SHA-1)来验证数据是否被篡改。SHA-1 最近遭受了一些攻击,但在用于 消息认证 的 HMAC 中仍然安全。
Play 需要有灵活性,能够根据需要迁移到不同的 HMAC 函数 as needed,因此不应该成为公共 API 的一部分。
§潜在的新功能
Play 目前签署会话 cookie,但不会在会话 cookie 中添加任何会话超时或过期日期。这意味着,在适当的打开情况下,主动攻击者可以将一个会话 cookie 替换为另一个会话 cookie。
§用作密码哈希的误用
请不要使用 Crypto.sign
或任何类型的 HMAC,因为它们不是为密码哈希设计的。MAC 的设计目的是快速廉价,而密码哈希应该是缓慢且昂贵的。请查看 scrypt、bcrypt 或 PBKDF2 - jBCrypt 特别是作为 Java 中的 bcrypt 实现而闻名。
§对称加密
Crypto 包含两种对称加密方法,Crypto.encryptAES
和 Crypto.decryptAES
。这些方法在 Play 内部没有使用,但 significant developer effort has 已经投入到审查这些方法中。这些方法将被弃用,并且可能在将来的版本中被删除。
正如警告中提到的,这些方法通常并不“安全” - 使用这些方法,一些常见的操作模式并不安全。以下是使用Crypto.encryptAES
的一些加密问题的简要描述。
再次强调,Crypto.encryptAES
在 Play 中从未直接使用,因此这不是 Play 本身的安全漏洞。
§使用无认证的流密码
Crypto.encryptAES
默认使用 AES-CTR,这是一种提供加密但不提供身份验证的 AES 模式 - 这意味着主动攻击者可以将加密文本的内容替换为其他内容。这种特性被称为“可塑性”,这意味着在某些条件下可以恢复明文并修改消息。有两种构造用于缓解这种情况:要么对加密文本进行签名(称为“先加密后 MAC”),要么可以使用经过身份验证的加密,例如 AES-GCM。
Play 使用Crypto.encryptAES
加密会话 cookie 的内容(已应用 MAC)。由于 Play 使用“先加密后 MAC”,因此它不会受到攻击 - 但是,使用对称加密而没有 MAC 的用户可能会受到攻击。
鼓励用户不要实现自己的“先加密后 MAC”构造。相反,这里合适的解决方案是经过身份验证的加密,它同时对数据进行身份验证和加密 - 有关详细信息,请参阅迁移部分。
§违反密钥分离原则
虽然使用 AES 和 HMAC 被认为是一种安全的构造,但 Play 使用密钥进行加密的方式存在问题。“密钥分离原则”指出,你应该只将一个密钥用于一个目的。在本例中,play.crypto.secret 不仅用于签名,还用于Crypto.encryptAES
中的加密。
对于使用Crypto.encryptAES
的客户,这里密钥使用混合不会立即导致安全漏洞。
“对于 HMAC 与 AES,没有已知的这种干扰。密码学家的普遍感觉是,AES 和 SHA-1(或 SHA-256)“足够不同”,因此使用相同的密钥进行 AES 和 HMAC/SHA-1 应该不会出现实际问题。” - https://crypto.stackexchange.com/a/8086.
一旦应用程序变得更大,密钥分离原则可能会以另一种方式被违反:如果Crypto.encryptAES
用于多个目的,建议使用单独的密钥。
§模式的全局配置
如上所述,AES 可以与各种操作模式一起使用。使用不同的模式可能需要不同的额外安全措施。
但是,Play 通过配置 play.crypto.aes.transformation 配置选项,提供了全局配置操作模式的功能。也就是说,它会影响整个应用程序,包括所有使用 Crypto.encryptAES
的库。因此,很难确切地知道更改该选项对整个应用程序的影响。
§迁移
从 Crypto 功能迁移有几种路径。按优先级排序,它们分别是 Kalium、Tink 或纯 JCA。
§Kalium
如果您控制生产环境中的二进制文件,并且没有对 NIST 认可算法的外部要求:请使用 Kalium,它是 libsodium 库的包装器。
如果您需要 Crypto.sign
的 MAC 替换,请使用 org.abstractj.kalium.keys.AuthenticationKey
,它实现了 HMAC-SHA512/256。
如果您想要 Crypto.encryptAES
的对称加密替换,请使用 org.abstractj.kalium.crypto.SecretBox
,它实现了 密钥认证加密。
请注意,Kalium 确实要求 安装 libsodium 二进制文件,最好是从您已验证的源代码安装。
§Tink
如果您正在寻找纯 Java 解决方案或依赖于 NIST 认可的算法,Tink 在 JCA 之上提供了一个高级加密库。请注意,Tink 的支持级别不如 libsodium/Kalium,因此建议使用 Kalium。
如果您需要 Crypto.sign
的 MAC 替换,请使用 com.google.crypto.tink.mac.MacKeyTemplates
。
如果您需要 Crypto.encryptAES
的对称加密替换,请使用 com.google.crypto.tink.aead.AeadKeyTemplates
。
§JCA
Kalium 和 Tink 都使用与 Crypto 不同的加密原语。对于打算从 Crypto 功能迁移而又不更改底层算法的用户来说,最好的选择可能是将 Crypto 库中的代码提取到用户级类中。
§进一步阅读
有一些关于加密设计的论文,讨论了加密 API 解决的一些问题以及所涉及的复杂性。
下一个: Play 2.4
发现此文档中的错误? 此页面的源代码可以在这里找到 这里。 在阅读了 文档指南 之后,请随时贡献拉取请求。 有问题或建议要分享? 转到 我们的社区论坛 与社区开始对话。