New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Switching SecureTokens to Base58 #18347
Switching SecureTokens to Base58 #18347
Conversation
cc @dhh, @chancancode |
Looks good to me, but I'll let someone in the know actually review the algorithm ;). |
# The result may contain A-Z, a-z, 0-9 | ||
# | ||
# p SecureRandom.base64 #=> "08101M3q0p1v0z2h21171Y413e2M3r0t" | ||
# p SecureRandom.base64 #=> "3D3H0M3m1T3c1E2P290I0C0t3f432t1J" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should be examples of base62
f55489c
to
36338d8
Compare
require 'active_support/core_ext/securerandom' | ||
class SecureRandomTest < ActiveSupport::TestCase | ||
def test_generate_base62_string | ||
puts SecureRandom.base62 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
puts
is not needed
An actual base62 encoder seems needlessly complicated for our purposes... see my comments on previous discussion. |
whats's of issue with use base64 instead of base62? |
@robertomiranda it contains characters we don't want. That was also discussed to death in the previous PR. |
@matthewd What are you suggesting as an alternative here? |
Something like: random_bytes(chars).unpack("C*").map do |byte|
idx = byte % 64
idx = random_number(62) if idx >= 62
BASE62_CHARS[idx]
end.join And having looked closer, I've just realised that @robertomiranda's implementation is actually Wrong: it's not generating a uniformly random string. |
module SecureRandom | ||
# SecureRandom.base62 generates a random base62 string. | ||
# | ||
# The argument _n_ specifies the length, in bytes, of the random number |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The argument is named bytes
, not n
.
The above is obviously assuming that ''.tap do |string|
length.times { string << BASE62_CHARS[random_number(62)] }
end |
Also consider readability for those who may want to write these strings down - some existing algorithms, for exampe, avoid uppercase I (i) as it looks like lowercase l (L), same with o/O/0. |
Assuming a uniform distribution, with Base58 at 24 characters we are still getting 140 random bits, which is still miles better than a random UUID, so I guess why not. It's useful for things like "write down these TFA lock out code in case you lost your phone", or "enter this manually if your phone can't scan this QR code" scenario.
IMO, "close enough" is a dangerous territory here. Once you are off the tracks, all bets are off and it's difficult to tell what you are up against (e.g. Is this still equivalent or better than a random UUID? How would we prove it?). It also makes it difficult to put any guarantee on it hence recommend using this API for any specific purpose. |
👍 on what Godfrey said.
|
@egilburg I'm obviously being thick, because that looks uniform to me. Care to expand? |
@matthewd perhaps I'm the thicker one here, but this is a bit confusing to me: idx = byte % 64
idx = random_number(62) if idx >= 62 It seems you get one of 64 variations, but 2 of those are "bad" so you "reroll" them to a backup implementation. Why would just |
256 values in a byte; I did offer a vastly simpler alternative that just uses |
dea0c7a
to
92c18db
Compare
af5b63d
to
4f77dc1
Compare
Update Secure Token Doc [ci skip] remove require securerandom, core_ext/securerandom already do that ref 7e00605
679e5d9
to
47316fe
Compare
Switching SecureTokens to Base58
ref #18217 (comment)