added delay anti enumeration for register
All checks were successful
Build and Push Docker Image / build-and-push (push) Successful in 10m26s
All checks were successful
Build and Push Docker Image / build-and-push (push) Successful in 10m26s
This commit is contained in:
parent
ff4c9eee87
commit
02eb0d7cf5
154
Cargo.lock
generated
154
Cargo.lock
generated
@ -40,7 +40,7 @@ checksum = "3c3610892ee6e0cbce8ae2700349fcf8f98adb0dbfbee85aec3c9179d29cc072"
|
||||
dependencies = [
|
||||
"base64ct",
|
||||
"blake2",
|
||||
"cpufeatures",
|
||||
"cpufeatures 0.2.17",
|
||||
"password-hash",
|
||||
]
|
||||
|
||||
@ -144,7 +144,7 @@ version = "0.10.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe"
|
||||
dependencies = [
|
||||
"digest",
|
||||
"digest 0.10.7",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -156,6 +156,15 @@ dependencies = [
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "block-buffer"
|
||||
version = "0.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cdd35008169921d80bc60d3d0ab416eecb028c4cd653352907921d95084790be"
|
||||
dependencies = [
|
||||
"hybrid-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.20.2"
|
||||
@ -190,6 +199,17 @@ version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
||||
|
||||
[[package]]
|
||||
name = "chacha20"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6f8d983286843e49675a4b7a2d174efe136dc93a18d69130dd18198a6c167601"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cpufeatures 0.3.0",
|
||||
"rand_core 0.10.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "chrono"
|
||||
version = "0.4.44"
|
||||
@ -219,6 +239,12 @@ version = "0.9.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8"
|
||||
|
||||
[[package]]
|
||||
name = "const-oid"
|
||||
version = "0.10.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a6ef517f0926dd24a1582492c791b6a4818a4d94e789a334894aa15b0d12f55c"
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation-sys"
|
||||
version = "0.8.7"
|
||||
@ -234,6 +260,15 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cpufeatures"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crc"
|
||||
version = "3.4.0"
|
||||
@ -283,13 +318,22 @@ dependencies = [
|
||||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crypto-common"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "77727bb15fa921304124b128af125e7e3b968275d1b108b379190264f4423710"
|
||||
dependencies = [
|
||||
"hybrid-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "der"
|
||||
version = "0.7.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb"
|
||||
dependencies = [
|
||||
"const-oid",
|
||||
"const-oid 0.9.6",
|
||||
"pem-rfc7468",
|
||||
"zeroize",
|
||||
]
|
||||
@ -309,12 +353,23 @@ version = "0.10.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
|
||||
dependencies = [
|
||||
"block-buffer",
|
||||
"const-oid",
|
||||
"crypto-common",
|
||||
"block-buffer 0.10.4",
|
||||
"const-oid 0.9.6",
|
||||
"crypto-common 0.1.7",
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4850db49bf08e663084f7fb5c87d202ef91a3907271aff24a94eb97ff039153c"
|
||||
dependencies = [
|
||||
"block-buffer 0.12.0",
|
||||
"const-oid 0.10.2",
|
||||
"crypto-common 0.2.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "displaydoc"
|
||||
version = "0.2.5"
|
||||
@ -512,6 +567,7 @@ dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"r-efi",
|
||||
"rand_core 0.10.1",
|
||||
"wasip2",
|
||||
"wasip3",
|
||||
]
|
||||
@ -569,7 +625,7 @@ version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e"
|
||||
dependencies = [
|
||||
"digest",
|
||||
"digest 0.10.7",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -626,6 +682,15 @@ version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
|
||||
|
||||
[[package]]
|
||||
name = "hybrid-array"
|
||||
version = "0.4.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08d46837a0ed51fe95bd3b05de33cd64a1ee88fc797477ca48446872504507c5"
|
||||
dependencies = [
|
||||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hyper"
|
||||
version = "1.9.0"
|
||||
@ -832,7 +897,7 @@ dependencies = [
|
||||
"getrandom 0.2.17",
|
||||
"js-sys",
|
||||
"pem",
|
||||
"rand",
|
||||
"rand 0.8.6",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"signature",
|
||||
@ -931,7 +996,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"digest",
|
||||
"digest 0.10.7",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -987,7 +1052,7 @@ dependencies = [
|
||||
"num-integer",
|
||||
"num-iter",
|
||||
"num-traits",
|
||||
"rand",
|
||||
"rand 0.8.6",
|
||||
"smallvec",
|
||||
"zeroize",
|
||||
]
|
||||
@ -1070,7 +1135,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166"
|
||||
dependencies = [
|
||||
"base64ct",
|
||||
"rand_core",
|
||||
"rand_core 0.6.4",
|
||||
"subtle",
|
||||
]
|
||||
|
||||
@ -1204,7 +1269,18 @@ checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"rand_chacha",
|
||||
"rand_core",
|
||||
"rand_core 0.6.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d2e8e8bcc7961af1fdac401278c6a831614941f6164ee3bf4ce61b7edb162207"
|
||||
dependencies = [
|
||||
"chacha20",
|
||||
"getrandom 0.4.2",
|
||||
"rand_core 0.10.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1214,7 +1290,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
"rand_core",
|
||||
"rand_core 0.6.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1226,6 +1302,12 @@ dependencies = [
|
||||
"getrandom 0.2.17",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69"
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.5.18"
|
||||
@ -1269,9 +1351,12 @@ dependencies = [
|
||||
"axum",
|
||||
"chrono",
|
||||
"dotenvy",
|
||||
"hex",
|
||||
"jsonwebtoken",
|
||||
"rand 0.10.1",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sha2 0.11.0",
|
||||
"sqlx",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
@ -1288,14 +1373,14 @@ version = "0.9.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b8573f03f5883dcaebdfcf4725caa1ecb9c15b2ef50c43a07b816e06799bb12d"
|
||||
dependencies = [
|
||||
"const-oid",
|
||||
"digest",
|
||||
"const-oid 0.9.6",
|
||||
"digest 0.10.7",
|
||||
"num-bigint-dig",
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
"pkcs1",
|
||||
"pkcs8",
|
||||
"rand_core",
|
||||
"rand_core 0.6.4",
|
||||
"signature",
|
||||
"spki",
|
||||
"subtle",
|
||||
@ -1399,8 +1484,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cpufeatures",
|
||||
"digest",
|
||||
"cpufeatures 0.2.17",
|
||||
"digest 0.10.7",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1410,8 +1495,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cpufeatures",
|
||||
"digest",
|
||||
"cpufeatures 0.2.17",
|
||||
"digest 0.10.7",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha2"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "446ba717509524cb3f22f17ecc096f10f4822d76ab5c0b9822c5f9c284e825f4"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cpufeatures 0.3.0",
|
||||
"digest 0.11.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1445,8 +1541,8 @@ version = "2.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de"
|
||||
dependencies = [
|
||||
"digest",
|
||||
"rand_core",
|
||||
"digest 0.10.7",
|
||||
"rand_core 0.6.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1544,7 +1640,7 @@ dependencies = [
|
||||
"percent-encoding",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sha2",
|
||||
"sha2 0.10.9",
|
||||
"smallvec",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
@ -1582,7 +1678,7 @@ dependencies = [
|
||||
"quote",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sha2",
|
||||
"sha2 0.10.9",
|
||||
"sqlx-core",
|
||||
"sqlx-mysql",
|
||||
"sqlx-postgres",
|
||||
@ -1605,7 +1701,7 @@ dependencies = [
|
||||
"bytes",
|
||||
"chrono",
|
||||
"crc",
|
||||
"digest",
|
||||
"digest 0.10.7",
|
||||
"dotenvy",
|
||||
"either",
|
||||
"futures-channel",
|
||||
@ -1622,11 +1718,11 @@ dependencies = [
|
||||
"memchr",
|
||||
"once_cell",
|
||||
"percent-encoding",
|
||||
"rand",
|
||||
"rand 0.8.6",
|
||||
"rsa",
|
||||
"serde",
|
||||
"sha1",
|
||||
"sha2",
|
||||
"sha2 0.10.9",
|
||||
"smallvec",
|
||||
"sqlx-core",
|
||||
"stringprep",
|
||||
@ -1662,10 +1758,10 @@ dependencies = [
|
||||
"md-5",
|
||||
"memchr",
|
||||
"once_cell",
|
||||
"rand",
|
||||
"rand 0.8.6",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sha2",
|
||||
"sha2 0.10.9",
|
||||
"smallvec",
|
||||
"sqlx-core",
|
||||
"stringprep",
|
||||
|
||||
@ -19,3 +19,6 @@ argon2 = "0.5.3"
|
||||
jsonwebtoken = { version = "10.3.0", features = ["rand"] }
|
||||
chrono = { version = "0.4.44", features = ["serde"] }
|
||||
uuid = { version = "1.23.1", features = ["serde", "v4"] }
|
||||
rand = "0.10.1"
|
||||
sha2 = "0.11.0"
|
||||
hex = "0.4.3"
|
||||
|
||||
@ -1,11 +1,17 @@
|
||||
use std::time::Instant;
|
||||
|
||||
use axum::Json;
|
||||
use chrono::{Duration, Utc};
|
||||
|
||||
use crate::controller::model::auth_model::*;
|
||||
use crate::db::repository::refresh_token_repository::create_refresh_token;
|
||||
use crate::db::repository::user_repository;
|
||||
use crate::errors::AppError;
|
||||
use crate::state::AppState;
|
||||
use crate::utils::anti_enumeration::anti_enumeration_delay;
|
||||
use crate::utils::hash;
|
||||
use crate::utils::jwt::generate_access_token;
|
||||
use crate::utils::refresh_token::generate_refresh_token;
|
||||
|
||||
pub async fn login(state: &AppState, req: LoginRequest) -> Result<Json<AuthResponse>, AppError> {
|
||||
todo!()
|
||||
@ -14,16 +20,29 @@ pub async fn register(
|
||||
state: &AppState,
|
||||
req: RegisterRequest,
|
||||
) -> Result<Json<AuthResponse>, AppError> {
|
||||
let start = Instant::now();
|
||||
let mut tx = state.db.begin().await?;
|
||||
{
|
||||
let user = user_repository::get_user_by_email(&mut *tx, &req.email).await?;
|
||||
if user.is_some() {
|
||||
// user already registered
|
||||
anti_enumeration_delay(start, 150, 300).await;
|
||||
return Err(AppError::Internal);
|
||||
}
|
||||
}
|
||||
let h = hash::hash(&req.password)?;
|
||||
let user = user_repository::create_user(&mut *tx, req.email, h).await?;
|
||||
let access_token = generate_access_token(user.id, &state.jwt_secret)?;
|
||||
|
||||
let (refresh_plain, refresh_hash) = generate_refresh_token();
|
||||
let expires_at = chrono::Utc::now() + Duration::days(7);
|
||||
create_refresh_token(&mut *tx, user.id, refresh_hash, expires_at).await?;
|
||||
|
||||
tx.commit().await?;
|
||||
todo!("generate jwt and refresh token")
|
||||
anti_enumeration_delay(start, 150, 300).await;
|
||||
// TODO: put refresh token in cookie
|
||||
Ok(Json(AuthResponse {
|
||||
access_token: access_token,
|
||||
refresh_token: refresh_plain,
|
||||
}))
|
||||
}
|
||||
|
||||
21
src/utils/anti_enumeration.rs
Normal file
21
src/utils/anti_enumeration.rs
Normal file
@ -0,0 +1,21 @@
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use rand::RngExt;
|
||||
use tokio::time::sleep;
|
||||
|
||||
/// Anti-enumeration: ensures consistent response timing regardless of outcome.
|
||||
/// Call at the end of request handler, before returning.
|
||||
/// Range: 150-350ms (configurable)
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `start` - The Instant when request processing began
|
||||
/// * `min_ms` - Minimum delay in milliseconds (default: 150)
|
||||
/// * `max_ms` - Maximum delay in milliseconds (default: 350)
|
||||
pub async fn anti_enumeration_delay(start: Instant, min_ms: u64, max_ms: u64) {
|
||||
let target = min_ms + rand::rng().random::<u64>() % (max_ms - min_ms);
|
||||
let target_duration = Duration::from_millis(target);
|
||||
|
||||
if let Some(remaining) = target_duration.checked_sub(start.elapsed()) {
|
||||
sleep(remaining).await;
|
||||
}
|
||||
}
|
||||
@ -1,2 +1,4 @@
|
||||
pub mod anti_enumeration;
|
||||
pub mod hash;
|
||||
pub mod jwt;
|
||||
pub mod refresh_token;
|
||||
|
||||
18
src/utils/refresh_token.rs
Normal file
18
src/utils/refresh_token.rs
Normal file
@ -0,0 +1,18 @@
|
||||
use rand::Rng;
|
||||
use sha2::{Digest, Sha256};
|
||||
pub fn generate_refresh_token() -> (String, String) {
|
||||
let mut bytes = [0u8; 32];
|
||||
|
||||
let mut thread_rng = rand::rng();
|
||||
thread_rng.fill_bytes(&mut bytes);
|
||||
|
||||
let plain = hex::encode(&bytes); // 64 hex chars for user
|
||||
let hash = {
|
||||
// SHA-256 for DB storage
|
||||
let mut hasher = Sha256::new();
|
||||
hasher.update(&plain);
|
||||
hex::encode(hasher.finalize())
|
||||
};
|
||||
|
||||
(plain, hash)
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user