Prevent user enumeration via timing attacks.

This commit is contained in:
Luca Palmieri 2021-08-30 18:27:56 +02:00
parent 5492da0b38
commit 80a286113a
2 changed files with 25 additions and 10 deletions

View file

@ -110,10 +110,20 @@ async fn validate_credentials(
credentials: Credentials, credentials: Credentials,
pool: &PgPool, pool: &PgPool,
) -> Result<uuid::Uuid, PublishError> { ) -> Result<uuid::Uuid, PublishError> {
let (user_id, expected_password_hash) = get_stored_credentials(&credentials.username, &pool) let mut user_id = None;
let mut expected_password_hash = "$argon2id$v=19$m=15000,t=2,p=1$\
gZiV/M1gPc22ElAH/Jh1Hw$\
CWOrkoo7oJBQ/iyh7uJ0LO2aLEfrHwTWllSAxT0zRno"
.to_string();
if let Some((stored_user_id, stored_password_hash)) =
get_stored_credentials(&credentials.username, &pool)
.await .await
.map_err(PublishError::UnexpectedError)? .map_err(PublishError::UnexpectedError)?
.ok_or_else(|| PublishError::AuthError(anyhow::anyhow!("Unknown username.")))?; {
user_id = Some(stored_user_id);
expected_password_hash = stored_password_hash;
}
spawn_blocking_with_tracing(move || { spawn_blocking_with_tracing(move || {
verify_password_hash(expected_password_hash, credentials.password) verify_password_hash(expected_password_hash, credentials.password)
@ -122,7 +132,7 @@ async fn validate_credentials(
.context("Failed to spawn blocking task.") .context("Failed to spawn blocking task.")
.map_err(PublishError::UnexpectedError)??; .map_err(PublishError::UnexpectedError)??;
Ok(user_id) user_id.ok_or_else(|| PublishError::AuthError(anyhow::anyhow!("Unknown username.")))
} }
#[tracing::instrument( #[tracing::instrument(

View file

@ -1,5 +1,5 @@
use argon2::password_hash::SaltString; use argon2::password_hash::SaltString;
use argon2::{Argon2, PasswordHasher}; use argon2::{Algorithm, Argon2, Params, PasswordHasher, Version};
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use sqlx::{Connection, Executor, PgConnection, PgPool}; use sqlx::{Connection, Executor, PgConnection, PgPool};
use uuid::Uuid; use uuid::Uuid;
@ -163,7 +163,12 @@ impl TestUser {
async fn store(&self, pool: &PgPool) { async fn store(&self, pool: &PgPool) {
let salt = SaltString::generate(&mut rand::thread_rng()); let salt = SaltString::generate(&mut rand::thread_rng());
let password_hash = Argon2::default() // Match production parameters
let password_hash = Argon2::new(
Algorithm::Argon2id,
Version::V0x13,
Params::new(15000, 2, 1, None).unwrap(),
)
.hash_password(self.password.as_bytes(), &salt) .hash_password(self.password.as_bytes(), &salt)
.unwrap() .unwrap()
.to_string(); .to_string();