Spec-Zone.ru › Ruby on Rails 7.0

класс ActiveSupport::MessageVerifier

Родитель:
Object

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

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

Запомнить меня:

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

В фильтре аутентификации:

id, time = @verifier.verify(cookies[:remember_me])
if Time.now < time
  self.current_user = User.find(id)
end

По умолчанию для сериализации сообщения используется Marshal. Если вы хотите использовать другой метод сериализации, вы можете установить сериализатор в хеш-опций при инициализации:

@verifier = ActiveSupport::MessageVerifier.new('s3Krit', serializer: YAML)

MessageVerifier по умолчанию создаёт HMAC подписи с использованием алгоритма хеширования SHA1. Если вы хотите использовать другой алгоритм хеширования, вы можете изменить его, предоставив :digest ключ в качестве опции при инициализации верификатора:

@verifier = ActiveSupport::MessageVerifier.new('s3Krit', digest: 'SHA256')

Ограничение сообщений определённым назначением

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

token = @verifier.generate("this is the chair", purpose: :login)

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

@verifier.verified(token, purpose: :login)    # => "this is the chair"
@verifier.verified(token, purpose: :shipping) # => nil
@verifier.verified(token)                     # => nil

@verifier.verify(token, purpose: :login)      # => "this is the chair"
@verifier.verify(token, purpose: :shipping)   # => ActiveSupport::MessageVerifier::InvalidSignature
@verifier.verify(token)                       # => ActiveSupport::MessageVerifier::InvalidSignature

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

token = @verifier.generate("the conversation is lively")
@verifier.verified(token, purpose: :scare_tactics) # => nil
@verifier.verified(token)                          # => "the conversation is lively"

@verifier.verify(token, purpose: :scare_tactics)   # => ActiveSupport::MessageVerifier::InvalidSignature
@verifier.verify(token)                            # => "the conversation is lively"

Установление срока действия сообщений

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

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

Тогда сообщения могут быть проверены и возвращены до времени истечения срока действия. После этого, метод 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, digest: nil, serializer: nil) Показать исходный код
# File activesupport/lib/active_support/message_verifier.rb, line 110
def initialize(secret, digest: nil, serializer: nil)
  raise ArgumentError, "Secret should not be nil." unless secret
  @secret = secret
  @digest = digest&.to_s || "SHA1"
  @serializer = serializer || Marshal
end

Методы публичного экземпляра

generate(value, expires_at: nil, expires_in: nil, purpose: nil) Показать исходный код
# File activesupport/lib/active_support/message_verifier.rb, line 188
def generate(value, expires_at: nil, expires_in: nil, purpose: nil)
  data = encode(Messages::Metadata.wrap(@serializer.dump(value), expires_at: expires_at, expires_in: expires_in, purpose: purpose))
  "#{data}#{SEPARATOR}#{generate_digest(data)}"
end

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

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

verifier = ActiveSupport::MessageVerifier.new 's3Krit'
verifier.generate 'a private message' # => "BAhJIhRwcml2YXRlLW1lc3NhZ2UGOgZFVA==--e2d724331ebdee96a10fb99b089508d1c72bd772"
valid_message?(signed_message) Показать исходный код
# File activesupport/lib/active_support/message_verifier.rb, line 126
def valid_message?(signed_message)
  data, digest = get_data_and_digest_from(signed_message)
  digest_matches_data?(digest, data)
end

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

verifier = ActiveSupport::MessageVerifier.new 's3Krit'
signed_message = verifier.generate 'a private 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(signed_message, purpose: nil, **) Показать исходный код
# File activesupport/lib/active_support/message_verifier.rb, line 152
def verified(signed_message, purpose: nil, **)
  data, digest = get_data_and_digest_from(signed_message)
  if digest_matches_data?(digest, data)
    begin
      message = Messages::Metadata.verify(decode(data), purpose)
      @serializer.load(message) if message
    rescue ArgumentError => argument_error
      return if argument_error.message.include?("invalid base64")
      raise
    end
  end
end

Декодирует подписанное сообщение, используя секрет MessageVerifier.

verifier = ActiveSupport::MessageVerifier.new 's3Krit'

signed_message = verifier.generate 'a private message'
verifier.verified(signed_message) # => 'a private message'

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

other_verifier = ActiveSupport::MessageVerifier.new 'd1ff3r3nt-s3Krit'
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
verify(*args, **options) Показать исходный код
# File activesupport/lib/active_support/message_verifier.rb, line 177
def verify(*args, **options)
  verified(*args, **options) || raise(InvalidSignature)
end

Декодирует подписанное сообщение, используя секрет MessageVerifier.

verifier = ActiveSupport::MessageVerifier.new 's3Krit'
signed_message = verifier.generate 'a private message'

verifier.verify(signed_message) # => 'a private message'

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

other_verifier = ActiveSupport::MessageVerifier.new 'd1ff3r3nt-s3Krit'
other_verifier.verify(signed_message) # => ActiveSupport::MessageVerifier::InvalidSignature

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

Spec-Zone.ru

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