mirror of
https://github.com/LukeMathWalker/zero-to-production.git
synced 2024-12-18 14:06:37 +00:00
Prevent user enumeration via timing attacks.
This commit is contained in:
parent
5492da0b38
commit
80a286113a
2 changed files with 25 additions and 10 deletions
|
@ -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;
|
||||||
.await
|
let mut expected_password_hash = "$argon2id$v=19$m=15000,t=2,p=1$\
|
||||||
.map_err(PublishError::UnexpectedError)?
|
gZiV/M1gPc22ElAH/Jh1Hw$\
|
||||||
.ok_or_else(|| PublishError::AuthError(anyhow::anyhow!("Unknown username.")))?;
|
CWOrkoo7oJBQ/iyh7uJ0LO2aLEfrHwTWllSAxT0zRno"
|
||||||
|
.to_string();
|
||||||
|
|
||||||
|
if let Some((stored_user_id, stored_password_hash)) =
|
||||||
|
get_stored_credentials(&credentials.username, &pool)
|
||||||
|
.await
|
||||||
|
.map_err(PublishError::UnexpectedError)?
|
||||||
|
{
|
||||||
|
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(
|
||||||
|
|
|
@ -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,10 +163,15 @@ 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
|
||||||
.hash_password(self.password.as_bytes(), &salt)
|
let password_hash = Argon2::new(
|
||||||
.unwrap()
|
Algorithm::Argon2id,
|
||||||
.to_string();
|
Version::V0x13,
|
||||||
|
Params::new(15000, 2, 1, None).unwrap(),
|
||||||
|
)
|
||||||
|
.hash_password(self.password.as_bytes(), &salt)
|
||||||
|
.unwrap()
|
||||||
|
.to_string();
|
||||||
sqlx::query!(
|
sqlx::query!(
|
||||||
"INSERT INTO users (user_id, username, password_hash)
|
"INSERT INTO users (user_id, username, password_hash)
|
||||||
VALUES ($1, $2, $3)",
|
VALUES ($1, $2, $3)",
|
||||||
|
|
Loading…
Reference in a new issue