Spec-Zone.ru › Ruby on Rails 8.1

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

Открытые методы класса

key_len (cipher = default_cipher) Показать исходный код
# File activesupport/lib/active_support/message_encryptor.rb, line 252
def self.key_len(cipher = default_cipher)
  OpenSSL::Cipher.new(cipher).key_len
end

Принимает шифр и возвращает длину его ключа, чтобы помочь создать ключ нужного размера.

new (secret, sign_secret = nil, **options) Показать исходный код
# 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.

Вызывает метод суперкласса

Открытые методы экземпляра

decrypt_and_verify (message, **options) Показать исходный код
# 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
encrypt_and_sign (value, **options) Show source
# 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.

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API