使用 attr_encrypted 生成相同密文的唯一 IV
Unique IVs producing identical ciphertext using attr_encrypted
使用最基本的设置:
class User < ActiveRecord::Base
attr_encrypted :name,
key: 'This is a key that is 256 bits!!',
encode: true,
encode_iv: true,
encode_salt: true
end
提供相同名称时,数据库中的结果如下所示:
╔════╦══════════════════════════════╦═══════════════════╗
║ id ║ encrypted_name ║ encrypted_name_iv ║
╠════╬══════════════════════════════╬═══════════════════╣
║ 1 ║ aVXZb1b317nroumXVBdV9pGxA2o= ║ JyE7wHups+3upY5e ║
║ 2 ║ aVXZb1b317nroumXVBdV9pGxA2o= ║ uz/ktrtbUAksg5Vp ║
╚════╩══════════════════════════════╩═══════════════════╝
为什么密文是一样的?这不是gem默认使用的iv点的一部分吗?
如果明文、密钥和IV相同则密文也相同
看来你需要使用:
attr_encrypted :email, key: 'some secret key', encode: true, encode_iv: true, encode_salt: true
注:encode_iv: true
或者设置默认选项encode_iv: true
更新: 下面是原文post解释了整个问题,现在问题已经解决,请看这个答案的底部一个解决方案。
我很确定你注意到 encryptor
gem 中的一个相当严重的安全问题(attr_encrypted
使用的 gem做实际的加密)。
问题是,当使用aes-256-gcm
算法(或任何AES GCM
算法时),初始化向量(IV)目前确实没有被考虑加密时。该问题不影响其他算法,但不幸的是 aes-256-gcm
是 attr_encrypted
.
中的默认算法
事实证明,it is the order of setting the IV vs. the encryption key what causes the issue. When IV is set before the key (as is in the gem),IV 未被考虑在内,但如果设置 after 密钥。
一些测试来证明问题:
在获取部分 encryptor
gem 代码时,我创建了最简单的测试用例来证明问题(在 ruby 2.3.0 下针对 OpenSSL 版本“1.0. 1f 2014 年 1 月 6 日"):
def base64_enc(bytes)
[bytes].pack("m")
end
def test_aes_encr(n, cipher, data, key, iv, iv_before_key = true)
cipher = OpenSSL::Cipher.new(cipher)
cipher.encrypt
# THIS IS THE KEY PART OF THE ISSUE
if iv_before_key
# this is how it's currently present in the encryptor gem code
cipher.iv = iv
cipher.key = key
else
# this is the version that actually works
cipher.key = key
cipher.iv = iv
end
if cipher.name.downcase.end_with?("gcm")
cipher.auth_data = ""
end
result = cipher.update(data)
result << cipher.final
puts "#{n} #{cipher.name}, iv #{iv_before_key ? "BEFORE" : "AFTER "} key: " +
"iv=#{iv}, result=#{base64_enc(result)}"
end
def test_encryption
data = "something private"
key = "This is a key that is 256 bits!!"
# control tests using AES-256-CBC
test_aes_encr(1, "aes-256-cbc", data, key, "aaaabbbbccccdddd", true)
test_aes_encr(2, "aes-256-cbc", data, key, "eeeeffffgggghhhh", true)
test_aes_encr(3, "aes-256-cbc", data, key, "aaaabbbbccccdddd", false)
test_aes_encr(4, "aes-256-cbc", data, key, "eeeeffffgggghhhh", false)
# failing tests using AES-256-GCM
test_aes_encr(5, "aes-256-gcm", data, key, "aaaabbbbcccc", true)
test_aes_encr(6, "aes-256-gcm", data, key, "eeeeffffgggg", true)
test_aes_encr(7, "aes-256-gcm", data, key, "aaaabbbbcccc", false)
test_aes_encr(8, "aes-256-gcm", data, key, "eeeeffffgggg", false)
end
运行 test_encryption
使用 AES-256-CBC
加密文本,然后使用 AES-256-GCM
,每次在两个机制中使用两个不同的 IV(IV 集 before/after 键),得到以下结果:
# control tests with CBC:
1 AES-256-CBC, iv BEFORE key: iv=aaaabbbbccccdddd, result=4IAGcazRmEUIRDE3ZpEgoS0Nmm1/+nrd5VT2/Xab0WM=
2 AES-256-CBC, iv BEFORE key: iv=eeeeffffgggghhhh, result=T7um2Wgb2vw1r4uryF3xnBeq+KozuetjKGItfNKurGE=
3 AES-256-CBC, iv AFTER key: iv=aaaabbbbccccdddd, result=4IAGcazRmEUIRDE3ZpEgoS0Nmm1/+nrd5VT2/Xab0WM=
4 AES-256-CBC, iv AFTER key: iv=eeeeffffgggghhhh, result=T7um2Wgb2vw1r4uryF3xnBeq+KozuetjKGItfNKurGE=
# the problematic tests with GCM:
5 id-aes256-GCM, iv BEFORE key: iv=aaaabbbbcccc, result=Tl/HfkWpwoByeYRz6Mz4yIo=
6 id-aes256-GCM, iv BEFORE key: iv=eeeeffffgggg, result=Tl/HfkWpwoByeYRz6Mz4yIo=
7 id-aes256-GCM, iv AFTER key: iv=aaaabbbbcccc, result=+4Iyn7RSDKimTQi0S3gn58E=
8 id-aes256-GCM, iv AFTER key: iv=eeeeffffgggg, result=3m9uEDyb9eh1RD3CuOCmc50=
这些测试表明,虽然设置 IV 与密钥的顺序与 CBC 无关,it is for GCM。更重要的是,CBC 中的加密结果对于两个不同的 IV 是不同的,而如果 IV 设置在密钥之前,则对于 GCM 则不是。
我刚刚创建了一个 pull request 来解决 encryptor
gem 中的这个问题。实际上,您现在有几个选择:
等新版本encryptor
gem发布。
也对 attr_encrypted
使用盐。无论如何,您应该使用 salt 来进一步保护加密数据。
非常不幸的是,所有已经加密的数据在修复后将变得无法破译,因为突然间 IV 将被考虑在内。
更新:encryptor
3.0.0 可用
您现在可以upgrade the encryptor
gem to version 3.0修复了错误。现在,如果这是您第一次使用 encryptor
或 attr_encrypted
gem,那么您已经准备就绪,一切都应该正常工作。
如果您的数据已使用 encryptor
2.0.0 加密,则 您必须在 gem 升级后手动重新加密 数据,否则无法正确解密!在 gem 升级期间,您将收到有关此的警告。流程示意图如下:
- 您必须使用
Encryptor
class(参见 README 的示例),使用 :v2_gcm_iv => true
选项解密所有加密数据。这应该可以正确解密您的数据。
- 然后您必须再次加密相同的数据,现在没有此选项(即
:v2_gcm_iv => false
)但包括来自您数据库的正确 IV。
- 如果您有生产数据,则需要在 gem 更新后立即进行离线升级,以确保不会损坏数据。
更新 2:openssl
gem 中的问题已确认并修复
仅供参考,最近 confirmed that this had actually been an issue in the underlying ruby-openssl library and the bug has been fixed 现在 。因此,在未来,甚至 attr_encrypted
gem 版本 2.x 与新的 openssl-2.0.0
gem 版本(即截至 2016 年 9 月,现在处于测试阶段。
使用最基本的设置:
class User < ActiveRecord::Base
attr_encrypted :name,
key: 'This is a key that is 256 bits!!',
encode: true,
encode_iv: true,
encode_salt: true
end
提供相同名称时,数据库中的结果如下所示:
╔════╦══════════════════════════════╦═══════════════════╗
║ id ║ encrypted_name ║ encrypted_name_iv ║
╠════╬══════════════════════════════╬═══════════════════╣
║ 1 ║ aVXZb1b317nroumXVBdV9pGxA2o= ║ JyE7wHups+3upY5e ║
║ 2 ║ aVXZb1b317nroumXVBdV9pGxA2o= ║ uz/ktrtbUAksg5Vp ║
╚════╩══════════════════════════════╩═══════════════════╝
为什么密文是一样的?这不是gem默认使用的iv点的一部分吗?
如果明文、密钥和IV相同则密文也相同
看来你需要使用:
attr_encrypted :email, key: 'some secret key', encode: true, encode_iv: true, encode_salt: true
注:encode_iv: true
或者设置默认选项encode_iv: true
更新: 下面是原文post解释了整个问题,现在问题已经解决,请看这个答案的底部一个解决方案。
我很确定你注意到 encryptor
gem 中的一个相当严重的安全问题(attr_encrypted
使用的 gem做实际的加密)。
问题是,当使用aes-256-gcm
算法(或任何AES GCM
算法时),初始化向量(IV)目前确实没有被考虑加密时。该问题不影响其他算法,但不幸的是 aes-256-gcm
是 attr_encrypted
.
事实证明,it is the order of setting the IV vs. the encryption key what causes the issue. When IV is set before the key (as is in the gem),IV 未被考虑在内,但如果设置 after 密钥。
一些测试来证明问题:
在获取部分 encryptor
gem 代码时,我创建了最简单的测试用例来证明问题(在 ruby 2.3.0 下针对 OpenSSL 版本“1.0. 1f 2014 年 1 月 6 日"):
def base64_enc(bytes)
[bytes].pack("m")
end
def test_aes_encr(n, cipher, data, key, iv, iv_before_key = true)
cipher = OpenSSL::Cipher.new(cipher)
cipher.encrypt
# THIS IS THE KEY PART OF THE ISSUE
if iv_before_key
# this is how it's currently present in the encryptor gem code
cipher.iv = iv
cipher.key = key
else
# this is the version that actually works
cipher.key = key
cipher.iv = iv
end
if cipher.name.downcase.end_with?("gcm")
cipher.auth_data = ""
end
result = cipher.update(data)
result << cipher.final
puts "#{n} #{cipher.name}, iv #{iv_before_key ? "BEFORE" : "AFTER "} key: " +
"iv=#{iv}, result=#{base64_enc(result)}"
end
def test_encryption
data = "something private"
key = "This is a key that is 256 bits!!"
# control tests using AES-256-CBC
test_aes_encr(1, "aes-256-cbc", data, key, "aaaabbbbccccdddd", true)
test_aes_encr(2, "aes-256-cbc", data, key, "eeeeffffgggghhhh", true)
test_aes_encr(3, "aes-256-cbc", data, key, "aaaabbbbccccdddd", false)
test_aes_encr(4, "aes-256-cbc", data, key, "eeeeffffgggghhhh", false)
# failing tests using AES-256-GCM
test_aes_encr(5, "aes-256-gcm", data, key, "aaaabbbbcccc", true)
test_aes_encr(6, "aes-256-gcm", data, key, "eeeeffffgggg", true)
test_aes_encr(7, "aes-256-gcm", data, key, "aaaabbbbcccc", false)
test_aes_encr(8, "aes-256-gcm", data, key, "eeeeffffgggg", false)
end
运行 test_encryption
使用 AES-256-CBC
加密文本,然后使用 AES-256-GCM
,每次在两个机制中使用两个不同的 IV(IV 集 before/after 键),得到以下结果:
# control tests with CBC:
1 AES-256-CBC, iv BEFORE key: iv=aaaabbbbccccdddd, result=4IAGcazRmEUIRDE3ZpEgoS0Nmm1/+nrd5VT2/Xab0WM=
2 AES-256-CBC, iv BEFORE key: iv=eeeeffffgggghhhh, result=T7um2Wgb2vw1r4uryF3xnBeq+KozuetjKGItfNKurGE=
3 AES-256-CBC, iv AFTER key: iv=aaaabbbbccccdddd, result=4IAGcazRmEUIRDE3ZpEgoS0Nmm1/+nrd5VT2/Xab0WM=
4 AES-256-CBC, iv AFTER key: iv=eeeeffffgggghhhh, result=T7um2Wgb2vw1r4uryF3xnBeq+KozuetjKGItfNKurGE=
# the problematic tests with GCM:
5 id-aes256-GCM, iv BEFORE key: iv=aaaabbbbcccc, result=Tl/HfkWpwoByeYRz6Mz4yIo=
6 id-aes256-GCM, iv BEFORE key: iv=eeeeffffgggg, result=Tl/HfkWpwoByeYRz6Mz4yIo=
7 id-aes256-GCM, iv AFTER key: iv=aaaabbbbcccc, result=+4Iyn7RSDKimTQi0S3gn58E=
8 id-aes256-GCM, iv AFTER key: iv=eeeeffffgggg, result=3m9uEDyb9eh1RD3CuOCmc50=
这些测试表明,虽然设置 IV 与密钥的顺序与 CBC 无关,it is for GCM。更重要的是,CBC 中的加密结果对于两个不同的 IV 是不同的,而如果 IV 设置在密钥之前,则对于 GCM 则不是。
我刚刚创建了一个 pull request 来解决 encryptor
gem 中的这个问题。实际上,您现在有几个选择:
等新版本
encryptor
gem发布。也对
attr_encrypted
使用盐。无论如何,您应该使用 salt 来进一步保护加密数据。
非常不幸的是,所有已经加密的数据在修复后将变得无法破译,因为突然间 IV 将被考虑在内。
更新:encryptor
3.0.0 可用
您现在可以upgrade the encryptor
gem to version 3.0修复了错误。现在,如果这是您第一次使用 encryptor
或 attr_encrypted
gem,那么您已经准备就绪,一切都应该正常工作。
如果您的数据已使用 encryptor
2.0.0 加密,则 您必须在 gem 升级后手动重新加密 数据,否则无法正确解密!在 gem 升级期间,您将收到有关此的警告。流程示意图如下:
- 您必须使用
Encryptor
class(参见 README 的示例),使用:v2_gcm_iv => true
选项解密所有加密数据。这应该可以正确解密您的数据。 - 然后您必须再次加密相同的数据,现在没有此选项(即
:v2_gcm_iv => false
)但包括来自您数据库的正确 IV。 - 如果您有生产数据,则需要在 gem 更新后立即进行离线升级,以确保不会损坏数据。
更新 2:openssl
gem 中的问题已确认并修复
仅供参考,最近 confirmed that this had actually been an issue in the underlying ruby-openssl library and the bug has been fixed 现在 。因此,在未来,甚至 attr_encrypted
gem 版本 2.x 与新的 openssl-2.0.0
gem 版本(即截至 2016 年 9 月,现在处于测试阶段。