Ответ 1
Обновление: следующее сообщение, объясняющее всю проблему, проблема исправлена, см. нижнюю часть этого ответа для решения.
Я уверен, что вы заметили довольно неприятную проблему безопасности в encryptor
gem (камень, который используется attr_encrypted
для выполнения фактических шифров).
Проблема заключается в том, что при использовании алгоритма aes-256-gcm
(или любого из алгоритмов AES GCM
) вектор инициализации (IV) в настоящее время действительно не учитывается при шифровании. Проблема не влияет на другие алгоритмы, но, к сожалению, aes-256-gcm
является алгоритмом по умолчанию в attr_encrypted
.
Как оказалось, это порядок установки IV против ключа шифрования, что вызывает вопрос. Когда IV задается перед ключом (поскольку находится в драгоценном камне), IV не учитывается, но он устанавливается после ключа.
Некоторые тесты для проверки проблемы:
Принимая части кода encryptor
gem, я создал простейший тестовый пример, чтобы доказать эту проблему (протестирован под ruby 2.3.0, скомпилированный с версией OpenSSL "1.0.1f 6 января 2014" ):
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 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 до/после ключа) дает нам следующие результаты:
# 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, это для GCM. Что еще более важно, зашифрованный результат в CBC отличается для двух разных IV, тогда как для GCM не требуется, если IV задан перед ключом.
Я только что создал запрос на перенос, чтобы исправить эту проблему в камне encryptor
. На практике у вас есть несколько вариантов:
-
Подождите, пока не будет выпущена новая версия
encryptor
. -
Используйте также соль с
attr_encrypted
. Вы должны использовать соль в любом случае для дальнейшей защиты зашифрованных данных.
Самое печальное, что все уже зашифрованные данные станут неподдающимися исправлению после исправления, так как внезапно будут приняты во внимание IV.
Обновление: encryptor
3.0.0
Теперь вы можете обновить камень encryptor
до версии 3.0, в котором исправлена ошибка. Теперь, если вы впервые используете драгоценные камни encryptor
или attr_encrypted
, вы все настроены, и все должно работать правильно.
Если у вас есть данные, которые уже зашифрованы с помощью encryptor
2.0.0, тогда вы должны вручную заново зашифровать данные после обновления gem, иначе он не сможет правильно расшифровать! Вы будете предупреждены об этом во время обновления жемчужины. Схематическая процедура следующая:
- Вам необходимо расшифровать все ваши зашифрованные данные с помощью класса
encryptor
(примеры README), используя опцию:v2_gcm_iv => true
, Это должно правильно расшифровать ваши данные. - Затем вы должны снова зашифровать одни и те же данные, без этой опции (т.е.
:v2_gcm_iv => false
), но включая правильный IV из вашей базы данных. - Если у вас есть производственные данные, вам нужно будет сделать это обновление в автономном режиме и сразу после обновления gem, чтобы гарантировать отсутствие повреждения данных.
Обновление 2: проблема в openssl
подтверждена и исправлена gem
FYI, недавно подтвердил, что это действительно была проблема в базовой библиотеке ruby-openssl и ошибка была исправлена сейчас. Таким образом, в будущем возможно, что даже attr_encrypted
gem version 2.x будет корректно работать при использовании с новой версией openssl-2.0.0
gem (которая теперь находится в бета-версии по состоянию на сентябрь 2016 года).