Last active
September 28, 2018 21:13
-
-
Save danielfone/c3ef94305bbb679e66d0b64fe567b547 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-- This is an effective but naive approach to selecting an authenticated user from a users table. | |
-- The problem is that the query is (intentionally) slow if it has to check a digest, | |
-- but finishes very quickly if the email doesn't match, and so provides a mechanism for user enumeration. | |
select * | |
from users | |
where email = '[email protected]' | |
and password_digest = crypt('password', password_digest); | |
-- The key goal is to provide a roughly constant time query whether the email or the password is wrong | |
with | |
-- Supply the id and digest of any user matching the email along with | |
-- a null id and a dummy password to compare | |
-- (the plaintext for the digest is irrelevant since the id will never match) | |
user_with_fallback as ( | |
select id, password_digest from users where email = '[email protected]' | |
union | |
select null, '$2a$10$hyyEri8US5gHpMv9XddSYe9pfzdBXsIlCxpfHYmbr5GYYHxDVgiMa' | |
), | |
-- Return the first (user id, password digest) tuple | |
user_or_fallback as ( | |
select * from user_with_fallback order by id nulls last limit 1 | |
), | |
-- select the id supplied above where the digest matches the supplied password | |
-- this is where the bulk of the query time is spent | |
matching_auth as ( | |
select id from user_or_fallback where password_digest = crypt('password', password_digest) | |
) | |
-- Query the user table based on the id which matches both the email and password | |
select * from users natural join matching_auth; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment