class 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, и может обеспечивать более высокую производительность. Однако для их работы необходим гемmsgpack.В Rails значение по умолчанию зависит от
config.active_support.message_serializer. В противном случае используется:marshal. :url_safe-
По умолчанию
MessageEncryptorсоздаёт строки, соответствующие RFC 4648, но не безопасные для URL. Иными словами, они могут содержать символы «+» и «/». Чтобы создавать строки, безопасные для URL (соответствующие разделу «Base 64 Encoding with URL and Filename Safe Alphabet» стандарта 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.