Spec-Zone.ru › Ruby on Rails 7.1

class ActiveSupport::MessageVerifier

Parent:
Messages::Codec

Active Support Message Verifier

MessageVerifier упрощает генерацию и проверку подписанных сообщений, предотвращая их подделку.

В приложении Rails вы можете использовать Rails.application.message_verifier для управления уникальными экземплярами верификаторов для каждого случая использования. Узнать больше.

Это полезно для случаев, таких как токены «запомнить меня» и ссылки на автоматическую отписку, когда хранилище сеансов не подходит или недоступно.

Сначала сгенерируйте подписанное сообщение:

cookies[:remember_me] = Rails.application.message_verifier(:remember_me).generate([@user.id, 2.weeks.from_now])

Затем проверьте это сообщение:

id, time = Rails.application.message_verifier(:remember_me).verify(cookies[:remember_me])
if time.future?
  self.current_user = User.find(id)
end

Ограничение сообщений конкретной целью

Не рекомендуется использовать один и тот же верификатор для разных целей в вашем приложении. Это может позволить злоумышленнику повторно использовать подписанное сообщение для выполнения несанкционированного действия. Вы можете снизить этот риск, ограничив подписанные сообщения конкретной :purpose.

token = @verifier.generate("signed message", purpose: :login)

Затем ту же самую цель необходимо передать при проверке, чтобы получить данные обратно:

@verifier.verified(token, purpose: :login)    # => "signed message"
@verifier.verified(token, purpose: :shipping) # => nil
@verifier.verified(token)                     # => nil

@verifier.verify(token, purpose: :login)      # => "signed message"
@verifier.verify(token, purpose: :shipping)   # => raises ActiveSupport::MessageVerifier::InvalidSignature
@verifier.verify(token)                       # => raises ActiveSupport::MessageVerifier::InvalidSignature

Аналогично, если у сообщения нет цели, оно не будет возвращено при проверке с указанной целью.

token = @verifier.generate("signed message")
@verifier.verified(token, purpose: :redirect) # => nil
@verifier.verified(token)                     # => "signed message"

@verifier.verify(token, purpose: :redirect)   # => raises ActiveSupport::MessageVerifier::InvalidSignature
@verifier.verify(token)                       # => "signed message"

Истекающие сообщения

По умолчанию сообщения действуют вечно, и проверка через год по-прежнему вернёт исходное значение. Но сообщения могут быть настроены на истечение в заданное время с помощью :expires_in или :expires_at.

@verifier.generate("signed message", expires_in: 1.month)
@verifier.generate("signed message", expires_at: Time.now.end_of_year)

Messages затем могут быть проверены и возвращены до истечения срока действия. После этого метод verified возвращает nil, а verify вызывает ActiveSupport::MessageVerifier::InvalidSignature.

Поочерёдное использование ключей

MessageVerifier также поддерживает отключение старых конфигураций, переходя к стеку верификаторов. Вызовите rotate для создания и добавления верификатора, поэтому verified или verify также будут пытаться выполнить проверку со значением по умолчанию.

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

Вы бы предоставили своему верификатору новые значения по умолчанию:

verifier = ActiveSupport::MessageVerifier.new(@secret, digest: "SHA512", serializer: JSON)

Затем постепенно замените старые значения, добавив их в качестве резервных. Любое сообщение, сгенерированное со старыми значениями, будет работать, пока не будет удалена замена.

verifier.rotate(old_secret)          # Fallback to an old secret instead of @secret.
verifier.rotate(digest: "SHA256")    # Fallback to an old digest instead of SHA512.
verifier.rotate(serializer: Marshal) # Fallback to an old serializer instead of JSON.

Хотя вышесказанное, скорее всего, будет объединённо в одну замену:

verifier.rotate(old_secret, digest: "SHA256", serializer: Marshal)

Методы публичного класса

new(secret, **options) Показать исходный код
# File activesupport/lib/active_support/message_verifier.rb, line 153
def initialize(secret, **options)
  raise ArgumentError, "Secret should not be nil." unless secret
  super(**options)
  @secret = secret
  @digest = options[:digest]&.to_s || "SHA1"
end

Инициализирует новый объект MessageVerifier с секретом для подписи.

Параметры

:digest

Digest используемый для подписи. По умолчанию "SHA1". См. OpenSSL::Digest для альтернатив.

: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, и может обеспечить улучшенную производительность. Однако для них требуется gem msgpack.

При использовании Rails, значение по умолчанию зависит от config.active_support.message_serializer. В противном случае значение по умолчанию – :marshal.

:url_safe

По умолчанию MessageVerifier генерирует строки, совместимые с 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.

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

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

generate(value, **options) Показать исходный код
# File activesupport/lib/active_support/message_verifier.rb, line 292
def generate(value, **options)
  create_message(value, **options)
end

Генерирует подписанное сообщение для предоставленного значения.

Сообщение подписывается с помощью секретного ключа MessageVerifier. Возвращает сообщение в Base64-кодировке, соединенное с сгенерированной подписью.

verifier = ActiveSupport::MessageVerifier.new("secret")
verifier.generate("signed message") # => "BAhJIhNzaWduZWQgbWVzc2FnZQY6BkVU--f67d5f27c3ee0b8483cebf2103757455e947493b"

Параметры

:expires_at

Дата и время истечения срока действия сообщения. После этой даты и времени проверка сообщения завершится неудачно.

message = verifier.generate("hello", expires_at: Time.now.tomorrow)
verifier.verified(message) # => "hello"
# 24 hours later...
verifier.verified(message) # => nil
verifier.verify(message)   # => raises ActiveSupport::MessageVerifier::InvalidSignature
:expires_in

Срок действия сообщения. После истечения этого срока проверка сообщения завершится неудачно.

message = verifier.generate("hello", expires_in: 24.hours)
verifier.verified(message) # => "hello"
# 24 hours later...
verifier.verified(message) # => nil
verifier.verify(message)   # => raises ActiveSupport::MessageVerifier::InvalidSignature
:purpose

Цель сообщения. Если указана, та же цель должна быть указана при проверке сообщения; в противном случае проверка завершится неудачно. (См. verified и verify.)

valid_message?(message) Показать исходный код
# File activesupport/lib/active_support/message_verifier.rb, line 169
def valid_message?(message)
  !!catch_and_ignore(:invalid_message_format) { extract_encoded(message) }
end

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

verifier = ActiveSupport::MessageVerifier.new("secret")
signed_message = verifier.generate("signed message")
verifier.valid_message?(signed_message) # => true

tampered_message = signed_message.chop # editing the message invalidates the signature
verifier.valid_message?(tampered_message) # => false
verified(message, **options) Показать исходный код
# File activesupport/lib/active_support/message_verifier.rb, line 210
def verified(message, **options)
  catch_and_ignore :invalid_message_format do
    catch_and_raise :invalid_message_serialization do
      catch_and_ignore :invalid_message_content do
        read_message(message, **options)
      end
    end
  end
end

Декодирует подписанное сообщение с использованием секретного ключа MessageVerifier.

verifier = ActiveSupport::MessageVerifier.new("secret")

signed_message = verifier.generate("signed message")
verifier.verified(signed_message) # => "signed message"

Возвращает nil, если сообщение не было подписано с тем же секретом.

other_verifier = ActiveSupport::MessageVerifier.new("different_secret")
other_verifier.verified(signed_message) # => nil

Возвращает nil, если сообщение не закодировано в Base64.

invalid_message = "f--46a0120593880c733a53b6dad75b42ddc1c8996d"
verifier.verified(invalid_message) # => nil

Возникает любая ошибка, возникшая при декодировании подписанного сообщения.

incompatible_message = "test--dad7b06c94abba8d46a15fafaef56c327665d5ff"
verifier.verified(incompatible_message) # => TypeError: incompatible marshal file format

Параметры

:purpose

Цель, с которой было сгенерировано сообщение. Если цель не совпадает, verified вернет nil.

message = verifier.generate("hello", purpose: "greeting")
verifier.verified(message, purpose: "greeting") # => "hello"
verifier.verified(message, purpose: "chatting") # => nil
verifier.verified(message)                      # => nil

message = verifier.generate("bye")
verifier.verified(message)                      # => "bye"
verifier.verified(message, purpose: "greeting") # => nil
verify(message, **options) Показать исходный код
# File activesupport/lib/active_support/message_verifier.rb, line 248
def verify(message, **options)
  catch_and_raise :invalid_message_format, as: InvalidSignature do
    catch_and_raise :invalid_message_serialization do
      catch_and_raise :invalid_message_content, as: InvalidSignature do
        read_message(message, **options)
      end
    end
  end
end

Декодирует подписанное сообщение с использованием секретного ключа MessageVerifier.

verifier = ActiveSupport::MessageVerifier.new("secret")
signed_message = verifier.generate("signed message")

verifier.verify(signed_message) # => "signed message"

Возникает InvalidSignature, если сообщение не было подписано с тем же секретом или не было закодировано в Base64.

other_verifier = ActiveSupport::MessageVerifier.new("different_secret")
other_verifier.verify(signed_message) # => ActiveSupport::MessageVerifier::InvalidSignature

Параметры

:purpose

Цель, с которой было сгенерировано сообщение. Если цель не совпадает, verify сгенерирует ActiveSupport::MessageVerifier::InvalidSignature.

message = verifier.generate("hello", purpose: "greeting")
verifier.verify(message, purpose: "greeting") # => "hello"
verifier.verify(message, purpose: "chatting") # => raises InvalidSignature
verifier.verify(message)                      # => raises InvalidSignature

message = verifier.generate("bye")
verifier.verify(message)                      # => "bye"
verifier.verify(message, purpose: "greeting") # => raises InvalidSignature

© 2004–2021 David Heinemeier Hansson
Licensed under the MIT License.

Spec-Zone.ru

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