【问题标题】:Will this CryptoJS AES encryption code produce secure output?这个 CryptoJS AES 加密代码会产生安全输出吗?
【发布时间】:2017-09-23 23:35:01
【问题描述】:

我相信因为传递给 AES.encrypt 的密钥是一个字符串,所以该函数会自动生成一个 IV。那么下面的代码是否会生成安全的 string_to_encrypt 加密版本?

pass  = document.getElementById('pass').value; // user entered pwrd    salt  = 'some system determined salt';
its   = 9000 + getKeyIterationModifier(pass); // iterations depend on pass
key   = CryptoJS.PBKDF2(pass, salt, { keySize: 512/32, iterations: its });

encrypted = CryptoJS.AES.encrypt(string_to_encrypt, key.toString());

或者我应该添加“模式”和“填充”值来进一步强化它吗?如果有,目前的行业标准值是多少?

换句话说,我是否应该在理想情况下使用 以下的东西(如果是自动完成的,也许没有 iv),如果是这样的话,什么是理想的:

key = CryptoJS.enc.Base64.parse(key);
encrypted = CryptoJS.AES.encrypt(string_to_encrypt, key, {
    iv: iv, 
    mode: CryptoJS.mode.CBC, 
    padding: CryptoJS.pad.Pkcs7
});

【问题讨论】:

  • 您确定用户输入的是“密钥”而不是“密码”吗?请参阅 point #6 并注意博客中的“基于 MD5 的糟糕算法”链接指向 @ArtjomB 撰写的 StackOverflow 帖子。 (回复您帖子的同一个人)。
  • 编辑的代码示例。用户确实在输入密码 - 根据您链接到的博客中的第 6 点,我正在使用 PBKDF2 将其转换为正确意义上的“密钥”。我现在已经编辑了第二个 sn-p,这样它就可以工作了——注意我必须对“密钥”进行编码才能使其工作;否则 JS 会发脾气。
  • @Claud 看起来更好,因为您正在使用自己的 PBKDF2 来弥补 CryptoJS 问题。唯一我不知道的是你是如何选择盐的。盐不应该是静态的,需要每次随机选择。此外,盐也不是秘密。您需要在解密时检索与以前相同的盐。
  • 目前我每次都使用相同的盐,从密码生成密钥。 (然后密钥被分成两部分,一半用于加密,一半用于 mac。加密过程每次都使用一个唯一的 iv,那么在这种情况下,这不是我的“盐”吗?(顺便说一句,TY 为您提供帮助! )
  • @Claud 不,很遗憾没有。密码的整个问题在于人们不善于选择密码,因此软件需要帮助确保如果两个人选择相同的密码(经常发生),那么他们不会得到相同的密钥。 salt 解决的问题与 IV 不同(salt 用于密钥安全,IV 用于加密安全)。请参阅crypto 101 的第 119 页。

标签: javascript aes padding cryptojs pbkdf2


【解决方案1】:

CryptoJS.AES.encrypt 使用 EVP_BytesToKey 将传递的“密钥”(视为密码)扩展为实际的 AES-256 密钥,如果“密钥”是字符串,则为 IV。它为此使用随机盐,因此密文是随机的。在您的第二次 sn-p 中,您需要自己处理 IV。

您实质上要问的是 EVP_BytesToKey 是否是安全密码扩展功能。这不是可以轻易回答的问题。它使用 MD5 的事实意味着第一个 sn-p 至少具有 AES-128 的安全性,这应该是好的。

另一个问题是,对于绝对每个加密,IV 都应该是不可预测的(阅读:随机)。不要使用静态 IV,因为这会使密码具有确定性,因此在语义上不安全。观察密文的攻击者可以确定之前发送相同消息前缀的时间。这个建议是在第一个 sn-p 中给出的,但是您必须在第二个 sn-p 中实现该实现,这可能会引入其他问题。

如果您对 CryptoJS 和密码学不满意,请使用第一个 sn-p。否则,尝试改进第二个sn-p。


安全注意事项:

如果您在浏览器中仅使用对称加密,您需要在服务器和客户端使用完全相同的密钥。如果您将加密密钥从服务器发送到客户端或以其他方式发送,您需要加密您的对称加密密钥。最简单的方法是使用 TLS。如果你使用 TLS,那么数据和密钥都是加密的,所以你不需要自己加密。这不提供任何安全性,只是有点混淆。你应该阅读:https://www.nccgroup.trust/us/about-us/newsroom-and-events/blog/2011/august/javascript-cryptography-considered-harmful/

您应该考虑通过诸如 GCM 或 EAX 之类的身份验证模式,或者通过使用具有强 MAC(例如 HMAC-SHA256)的 encrypt-then-MAC 方案来集成经过身份验证的加密。

【讨论】:

  • 谢谢!事实上,我的应用程序中的服务器(基于浏览器的个人密码和笔记存储系统)仅充当 Web 可访问的存储库 - 它永远不需要解密数据。
  • 我的加密现在将 PBKD2F 分成两半,对两半进行编码,并使用它们按照 stackexchange 答案中的“旧方式”精确地加密-然后-mac:security.stackexchange.com/a/63134/146741
  • 我唯一要补充的是,我认为 EVP_BytesToKey() 不能被视为安全密码扩展功能。从密码派生密钥需要在算法上很慢才能阻止密码猜测攻击,这似乎并不慢(基于您在另一个堆栈溢出帖子中放置的逆向工程)。 pbkdf2、bcrypt、scrypt、argon2等函数比较合适。
  • 我现在将第二种样式 sn-p(因此不再是 EVP_BytesToKey)与 pbkdf2 结合使用,它在 10,000 标记附近运行可变数量的迭代。处理速度快的 Mac 大约需要 1 秒。
  • @Claud 请记住,我的评估假设是第二个 sn-p 也使用由 PBKDF2 生成的key。此外,CBC 模式和 PKCK#7 填充是 CryptoJS 中的默认值,但明确定义它并没有什么坏处。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-04-01
  • 1970-01-01
  • 1970-01-01
  • 2016-12-17
  • 1970-01-01
相关资源
最近更新 更多