Skip to content

Commit ad20d9e

Browse files
Feliperafaelfranca
authored andcommitted
Improve ActiveSupport::MessageVerifier and ActiveRecord::SignedId docs
The documentation on ActiveSupport::MessageVerifier used the “sensitive data” string as an example; that wording might induce the developer to think we’re dealing with encryption, while the payload is actually only Base64 encoded and is not protected at all. We also improve the documentation on ActiveRecord::SignedId, which uses MessageVerifier and thereby will also expose the ID as encoded cleartext, making explicit that it’s not encryption, only signing. Lastly, we refer the developer to MessageEncryptor if the payload needs to be encrypted.
1 parent 9fdaea5 commit ad20d9e

File tree

3 files changed

+29
-5
lines changed

3 files changed

+29
-5
lines changed

activerecord/lib/active_record/signed_id.rb

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,16 @@ def combine_signed_id_purposes(purpose)
106106

107107

108108
# Returns a signed id that's generated using a preconfigured +ActiveSupport::MessageVerifier+ instance.
109+
#
109110
# This signed id is tamper proof, so it's safe to send in an email or otherwise share with the outside world.
111+
# However, as with any message signed with a +ActiveSupport::MessageVerifier+,
112+
# {the signed id is not encrypted}[link:classes/ActiveSupport/MessageVerifier.html#class-ActiveSupport::MessageVerifier-label-Signing+is+not+encryption].
113+
# It's just encoded and protected against tampering.
114+
#
115+
# This means that the ID can be decoded by anyone; however, if tampered with (so to point to a different ID),
116+
# the cryptographic signature will no longer match, and the signed id will be considered invalid and return nil
117+
# when passed to +find_signed+ (or raise with +find_signed!+).
118+
#
110119
# It can furthermore be set to expire (the default is not to expire), and scoped down with a specific purpose.
111120
# If the expiration date has been exceeded before +find_signed+ is called, the id won't find the designated
112121
# record. If a purpose is set, this too must match.

activesupport/lib/active_support/message_verifier.rb

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,18 @@ module ActiveSupport
3030
# self.current_user = User.find(id)
3131
# end
3232
#
33+
# === Signing is not encryption
34+
#
35+
# The signed messages are not encrypted. The payload is merely encoded (Base64 by default) and can be decoded by
36+
# anyone. The signature is just assuring that the message wasn't tampered with. For example:
37+
#
38+
# message = Rails.application.message_verifier('my_purpose').generate('never put secrets here')
39+
# # => "BAhJIhtuZXZlciBwdXQgc2VjcmV0cyBoZXJlBjoGRVQ=--a0c1c0827919da5e949e989c971249355735e140"
40+
# Base64.decode64(message.split("--").first) # no key needed
41+
# # => 'never put secrets here'
42+
#
43+
# If you also need to encrypt the contents, you must use ActiveSupport::MessageEncryptor instead.
44+
#
3345
# === Confine messages to a specific purpose
3446
#
3547
# It's not recommended to use the same verifier for different purposes in your application.

railties/lib/rails/application.rb

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -212,17 +212,20 @@ def message_verifiers
212212
# It is recommended not to use the same verifier for different things, so you can get different
213213
# verifiers passing the +verifier_name+ argument.
214214
#
215+
# For instance, +ActiveStorage::Blob.signed_id_verifier+ is implemented using this feature, which assures that
216+
# the IDs strings haven't been tampered with and are safe to use in a finder.
217+
#
218+
# See the ActiveSupport::MessageVerifier documentation for more information.
219+
#
215220
# ==== Parameters
216221
#
217222
# * +verifier_name+ - the name of the message verifier.
218223
#
219224
# ==== Examples
220225
#
221-
# message = Rails.application.message_verifier('sensitive_data').generate('my sensible data')
222-
# Rails.application.message_verifier('sensitive_data').verify(message)
223-
# # => 'my sensible data'
224-
#
225-
# See the ActiveSupport::MessageVerifier documentation for more information.
226+
# message = Rails.application.message_verifier('my_purpose').generate('data to sign against tampering')
227+
# Rails.application.message_verifier('my_purpose').verify(message)
228+
# # => 'data to sign against tampering'
226229
def message_verifier(verifier_name)
227230
message_verifiers[verifier_name]
228231
end

0 commit comments

Comments
 (0)