Closed oaosman84 closed 10 months ago
I suspect it's purposely done this way to avoid a timing attack - attackers could guess a valid token digest by observing how long database lookups for the token take, whereas this code only checks the digest using compare_digest
, to ensure the comparison is constant-time.
What @angusholder said and also I replied to you here https://github.com/jazzband/django-rest-knox/issues/245#issuecomment-1880812373 - no need for a duplicate ;-) closing for the same reason as on the other ticket.
I suspect it's purposely done this way to avoid a timing attack - attackers could guess a valid token digest by observing how long database lookups for the token take, whereas this code only checks the digest using
compare_digest
, to ensure the comparison is constant-time.
Granted, I'm not a security expert, but I'm not sure timing attacks apply here. Timing attacks usually work via byte-by-byte manipulation, but in this case we would:
1 will always take constant time. 2 may vary, but it is based on the hashed value, so the attacker really has no way to iterate byte-by-byte to get closer to the actual digest... furthermore, even if they do have the digest, they wouldn't be able to utilize it because they can only send a token, and the token must be hashed (that's the reason for storing the digest rather than the token in the database to begin with).
In fact, if we are worried about timing attacks on database queries, querying on the token_key itself might be leaking some info as that one is able to be manipulated byte-by-byte, and the attacker could presumably iterate their way into at least the token_key prefix.
Happy to be enlightened if I'm missing something.
@oaosman84 thanks a lot for elaborating more clearly.
My concern is still with the fact that we would constantly pre-generate potentially invalid digest for no good reasons (1).
@oaosman84 thanks a lot for elaborating more clearly.
My concern is still with the fact that we would constantly pre-generate potentially invalid digest for no good reasons (1).
OK, that's fair. I'll think about this a bit more, maybe there's a way to try to get the best of both worlds..
Thanks for clarifying.
In
authenticate_credentials
, there is a query to pull allAuthToken
s filtered bytoken_key
, then compare digests in Python. I believe this was necessary whensalt
was part of the model, because we needed thesalt
to generate the digest... but right now, seeing assalt
is not being used, couldn't we just pre-calculate the digest and then do a.get(token_key, digest)
? Any reason we need to pull allAuthToken
s matching thetoken_key
into Python and check them one by one?