Skip to content
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

Possible timing attack in magic link token lookup? #31

Closed
abevoelker opened this issue Nov 1, 2018 · 3 comments
Closed

Possible timing attack in magic link token lookup? #31

abevoelker opened this issue Nov 1, 2018 · 3 comments

Comments

@abevoelker
Copy link

I'm no crypto expert so pardon me if I'm wrong but it looks like the magic link lookup process is vulnerable to timing attacks due to the token lookup being a naive database lookup of the input string:

(See some considerations based on a previous vulnerability in Devise token auth here: https://stackoverflow.com/a/18695244/215168)

@abevoelker
Copy link
Author

I was thinking of possible suggestions for quick fixes to this like doing lookups based on cryptographic digest hash (would have to add this column):

def find_session
  Session.valid.find_by!(
    authenticatable_type: authenticatable_classname,
    token_hash: ::Digest::SHA256.hexdigest(params.fetch(:token, "")),
  )
end

Or alternatively using the existing BCrypt dependency for that column, or just using ActiveSupport::SecurityUtils.secure_compare to do constant-time compares without any new column or hashing. However either of these latter two I believe you couldn't use find_by! and would require an additional param to the magic link to identify the specific authenticatable resource you'd be looping over each token for.

However looking at how the gem works, I'm wondering if you could solve the whole thing by throwing away the passwordless_sessions table entirely and just using ActiveSupport::MessageEncryptor to generate encrypted tokens that contain the data you normally store in that table. What you'd give up is the ability to revoke/expire individual magic links by manipulating the database (instead you could only invalidate all existing magic links at once by rolling the secret key). Not sure how much utility there really is there anyway since the tokens appear to only be valid for one hour by default? But if you wanted to keep the table you could just use MessageEncryptor to encrypt the id of the row, and safely do Session.valid.find after decryption without worrying about timing issues.

@mikker
Copy link
Owner

mikker commented Nov 2, 2018

Hi @abevoelker! If you look a line above where find_session is called you’ll see I already do a Bcrypt hash to avoid something like what you describe.

I think the current model is sufficient. But if you feel there’s an improvement in what you’re suggesting, PRs are welcome 😊

@abevoelker
Copy link
Author

Hi @mikker, I did see that but it registered to my brain as effectively just a sleep since the output is discarded, which I've seen it said doesn't prevent timing attacks, just maybe slow them down a little:

https://security.stackexchange.com/questions/96489/can-i-prevent-timing-attacks-with-random-delays
https://stackoverflow.com/questions/28395665/could-a-random-sleep-prevent-timing-attacks

Anyway this level of paranoia isn't really that important for my use of the gem, especially given the 1hr token timeout, so I'm gonna close this. Have a great weekend! 🎉

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants