Generate API tokens with a secure RNG, store hashed #2637
Merged
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Note: The contents of this PR have already been deployed to production, as this was a security issue. The code in this PR is the minimum number of changes needed to rebase what was deployed today onto master. Unfortunately, since this was done in parallel with some refactoring to the errors module, this means some of the added code doesn't look like the rest of the code base. However, master currently does not reflect what is in production, so any cleanup should come as a followup PR. The purpose of this is mainly to get master in sync with prod after going through bors.
This addresses a security issue with the way crates.io generates API
tokens. Prior to this commit, they were generated using the PostgreSQL
random
function, which is not a secure PRNG. Additionally, the tokenswere stored in plain text, which would give an attacker who managed to
compromise our database access to all user's API tokens. This commit
addresses both changes.
An advisory about the problem was posted at
https://blog.rust-lang.org/2020/07/14/crates-io-security-advisory.html
The tokens are now generated using the OS's random number generator,
which maps to C's
getrandom
function, which is secure andunpredictable.
The tokens are hashed using sha256. The choice to use a fast hashing
function such as this one instead of one typically used for passwords
(such as bcrypt or argon2) was intentional. Unlike passwords, our API
tokens are known to be 32 characters and are truly random, giving us 192
bits of entropy. This means that even with a fast hashing function,
actually finding a token from that hash before the death of human
civilization is infeasible.
Additionally, unlike passwords, API tokens need to be checked on every
request where they're used, instead of once at sign in. This means that
a slower hashing function would put significantly more load on our
server than they would when used for passwords.
We opted to use sha256 instead of bcrypt with a lower cost due to the
mandatory salt in bcrypt. If we salt the values before hashing them, the
tokens can no longer directly be used to identify themselves, and we
would need to include another identifier in the token given to the user.
While this is feasible, it leads to a very obtuse looking token, and
more complex code.
r? @jtgeibel