класс ActiveSupport::MessageEncryptor
Шифровальщик сообщений Active Support
MessageEncryptor — простой способ шифрования значений, которые хранятся в ненадежном месте.
Шифрованный текст и вектор инициализации кодируются в base64 и возвращаются вам.
Это можно использовать в ситуациях, аналогичных MessageVerifier, но при этом пользователи не смогут определить значение полезной нагрузки.
len = ActiveSupport::MessageEncryptor.key_len
salt = SecureRandom.random_bytes(len)
key = ActiveSupport::KeyGenerator.new('password').generate_key(salt, len) # => "\x89\xE0\x156\xAC..."
crypt = ActiveSupport::MessageEncryptor.new(key) # => #<ActiveSupport::MessageEncryptor ...>
encrypted_data = crypt.encrypt_and_sign('my secret data') # => "NlFBTTMwOUV5UlA1QlNEN2xkY2d6eThYWWh..."
crypt.decrypt_and_verify(encrypted_data) # => "my secret data"
Метод decrypt_and_verify вызовет исключение ActiveSupport::MessageEncryptor::InvalidMessage, если предоставленные данные нельзя расшифровать или проверить.
crypt.decrypt_and_verify('not encrypted data') # => ActiveSupport::MessageEncryptor::InvalidMessage
Ограничение сообщений конкретной целью
По умолчанию любое сообщение может использоваться в приложении. Но сообщения также могут быть ограничены определённой :purpose.
token = crypt.encrypt_and_sign("this is the chair", purpose: :login)
Затем при получении данных обратно нужно передать ту же цель при проверке:
crypt.decrypt_and_verify(token, purpose: :login) # => "this is the chair" crypt.decrypt_and_verify(token, purpose: :shipping) # => nil crypt.decrypt_and_verify(token) # => nil
Также, если у сообщения нет цели, оно не будет возвращено при проверке с указанием конкретной цели.
token = crypt.encrypt_and_sign("the conversation is lively")
crypt.decrypt_and_verify(token, purpose: :scare_tactics) # => nil
crypt.decrypt_and_verify(token) # => "the conversation is lively"
Установка срока действия сообщений
По умолчанию сообщения действительны постоянно, и проверка через год всё ещё вернёт исходное значение. Но срок действия сообщений может быть установлен на определённое время с помощью :expires_in или :expires_at.
crypt.encrypt_and_sign(parcel, expires_in: 1.month) crypt.encrypt_and_sign(doowad, expires_at: Time.now.end_of_year)
Затем сообщения можно проверить и вернуть до истечения срока действия. После этого проверка вернёт nil.
Вращение ключей
MessageEncryptor также поддерживает вращение старых конфигураций путём использования стека шифровальщиков. Вызовите rotate для построения и добавления шифровальщика, чтобы decrypt_and_verify также использовал резервную копию.
По умолчанию все вращаемые шифровальщики используют значения основного шифровальщика, если не указано иное.
Вы бы задали новые значения по умолчанию для своего шифровальщика:
crypt = ActiveSupport::MessageEncryptor.new(@secret, cipher: "aes-256-gcm")
Затем постепенно выведите из использования старые значения, добавив их как резервные копии. Любое сообщение, сгенерированное со старыми значениями, будет работать до тех пор, пока вращение не будет удалено.
crypt.rotate old_secret # Fallback to an old secret instead of @secret. crypt.rotate cipher: "aes-256-cbc" # Fallback to an old cipher instead of aes-256-gcm.
Хотя если и секрет, и шифр были изменены одновременно, вышеуказанное должно быть объединено в:
crypt.rotate old_secret, cipher: "aes-256-cbc"
Константы
- OpenSSLCipherError
Публичные методы класса
# File activesupport/lib/active_support/message_encryptor.rb, line 252 def self.key_len(cipher = default_cipher) OpenSSL::Cipher.new(cipher).key_len end
Для заданного шифра возвращает длину ключа шифра, чтобы помочь в генерации ключа нужной длины.
# File activesupport/lib/active_support/message_encryptor.rb, line 183
def initialize(secret, sign_secret = nil, **options)
super(**options)
@secret = secret
@cipher = options[:cipher] || self.class.default_cipher
@aead_mode = new_cipher.authenticated?
@verifier = if !@aead_mode
MessageVerifier.new(sign_secret || secret, **options, serializer: NullSerializer)
end
end Инициализирует новый MessageEncryptor. secret должен быть не меньше размера ключа шифра. Для шифра по умолчанию ‘aes-256-gcm’ он составляет 256 бит. Если вы используете секрет, введённый пользователем, вы можете сгенерировать подходящий ключ, используя ActiveSupport::KeyGenerator или аналогичную функцию вывода ключа.
Первый дополнительный параметр используется в качестве ключа подписи для MessageVerifier. Это позволяет вам указать ключи для шифрования и подписи данных. Игнорируется при использовании шифра AEAD, такого как ‘aes-256-gcm’.
ActiveSupport::MessageEncryptor.new('secret', 'signature_secret')
Параметры
-
:cipher -
Используемый шифр. Может быть любым шифром, возвращаемым
OpenSSL::Cipher.ciphers. По умолчанию ‘aes-256-gcm’. -
:digest -
Digestиспользуемый для подписи. Игнорируется при использовании шифра AEAD, такого как ‘aes-256-gcm’. -
:serializer -
Сериализатор, используемый для сериализации данных сообщения. Вы можете указать любой объект, который отвечает на
dumpиload, или выбрать один из нескольких предварительно настроенных сериализаторов::marshal,:json_allow_marshal,:json,:message_pack_allow_marshal,:message_pack.Предварительно настроенные сериализаторы включают механизм обратного вызова для поддержки нескольких форматов десериализации. Например, сериализатор
:marshalбудет сериализовать с использованиемMarshal, но может десериализовать с использованиемMarshal,ActiveSupport::JSONилиActiveSupport::MessagePack. Это упрощает миграцию между сериализаторами.Сериализаторы
:marshal,:json_allow_marshal, и:message_pack_allow_marshalподдерживают десериализацию с использованиемMarshal, но другие — нет. Имейте в виду, чтоMarshalявляется потенциальным вектором атак десериализации в случаях, когда был скомпрометирован секрет подписи сообщения. Если возможно, выберите сериализатор, который не поддерживаетMarshal.Сериализаторы
:message_packи:message_pack_allow_marshalиспользуютActiveSupport::MessagePack, который может обращать в обратном направлении некоторые типы Ruby, которые не поддерживаютсяJSON, и может обеспечить улучшенную производительность. Однако для них требуется gemmsgpack.При использовании Rails значение по умолчанию зависит от
config.active_support.message_serializer. В противном случае значением по умолчанию является:marshal. -
:url_safe -
По умолчанию
MessageEncryptorгенерирует строки, совместимые с RFC 4648, которые не являются URL-безопасными. Другими словами, они могут содержать “+” и “/”. Если вы хотите сгенерировать URL-безопасные строки (в соответствии с «Кодирование Base 64 с URL и безопасностью имён файлов» в RFC 4648), вы можете передатьtrue. -
:force_legacy_metadata_serializer -
Использовать ли устаревший сериализатор метаданных, который сериализует сообщение сначала, а затем оборачивает его в конверт, который также сериализуется. Это было значением по умолчанию в Rails 7.0 и ниже.
Если вы не передадите истинное значение, значение по умолчанию устанавливается с помощью
config.active_support.use_message_serializer_for_metadata.
Публичные методы экземпляра
# File activesupport/lib/active_support/message_encryptor.rb, line 241
def decrypt_and_verify(message, **options)
catch_and_raise :invalid_message_format, as: InvalidMessage do
catch_and_raise :invalid_message_serialization, as: InvalidMessage do
catch_and_ignore :invalid_message_content do
read_message(message, **options)
end
end
end
end Расшифровывает и проверяет сообщение. Мы должны проверить сообщение, чтобы избежать атак с подделкой заполнения. См.: www.limited-entropy.com/padding-oracle-attacks/.
Параметры
-
:purpose -
Цель, с которой было сгенерировано сообщение. Если цель не совпадает,
decrypt_and_verifyвернётnil.message = encryptor.encrypt_and_sign("hello", purpose: "greeting") encryptor.decrypt_and_verify(message, purpose: "greeting") # => "hello" encryptor.decrypt_and_verify(message) # => nil message = encryptor.encrypt_and_sign("bye") encryptor.decrypt_and_verify(message) # => "bye" encryptor.decrypt_and_verify(message, purpose: "greeting") # => nil
# File activesupport/lib/active_support/message_encryptor.rb, line 220 def encrypt_and_sign(value, **options) create_message(value, **options) end
Шифрует и подписывает сообщение. Мы должны подписать сообщение, чтобы избежать атак с подделкой заполнения. См.: www.limited-entropy.com/padding-oracle-attacks/.
Параметры
-
:expires_at -
Дата и время истечения срока действия сообщения. После этой даты и времени проверка сообщения завершится неудачей.
message = encryptor.encrypt_and_sign("hello", expires_at: Time.now.tomorrow) encryptor.decrypt_and_verify(message) # => "hello" # 24 hours later... encryptor.decrypt_and_verify(message) # => nil -
:expires_in -
Продолжительность действия сообщения. После истечения этого срока проверка сообщения завершится неудачей.
message = encryptor.encrypt_and_sign("hello", expires_in: 24.hours) encryptor.decrypt_and_verify(message) # => "hello" # 24 hours later... encryptor.decrypt_and_verify(message) # => nil -
:purpose -
Цель сообщения. Если указана, та же цель должна быть указана при проверке сообщения; в противном случае проверка завершится неудачей. (См.
decrypt_and_verify.)
© 2004–2021 David Heinemeier Hansson
Licensed under the MIT License.