From bc7866b4fb2c860fd2bf19639021f706f21c2752 Mon Sep 17 00:00:00 2001 From: Dmitri Date: Sun, 3 May 2026 11:22:37 +0200 Subject: [PATCH] removed middlewares --- .../middleware/AntiEnumerationLayer.rs | 73 -------------- src/controller/middleware/RateLimitLayer.rs | 97 ------------------- src/controller/middleware/mod.rs | 2 - src/controller/mod.rs | 1 - src/controller/v1/auth_controller.rs | 19 +--- 5 files changed, 2 insertions(+), 190 deletions(-) delete mode 100644 src/controller/middleware/AntiEnumerationLayer.rs delete mode 100644 src/controller/middleware/RateLimitLayer.rs delete mode 100644 src/controller/middleware/mod.rs diff --git a/src/controller/middleware/AntiEnumerationLayer.rs b/src/controller/middleware/AntiEnumerationLayer.rs deleted file mode 100644 index 1632388..0000000 --- a/src/controller/middleware/AntiEnumerationLayer.rs +++ /dev/null @@ -1,73 +0,0 @@ -use axum::{extract::Request, response::Response}; -use futures_util::future::BoxFuture; -use std::task::{Context, Poll}; -use std::time::{Duration, Instant}; -use tokio::time::sleep; -use tower::{Layer, Service}; -use rand::RngExt; - -/// Middleware Layer that ensures every request takes a minimum amount of time. -/// This prevents "Timing Attacks" where an attacker can determine if a user exists -/// by observing how much faster the server responds when an email is not found -/// versus when a password check (expensive hash) is performed. -#[derive(Clone)] -pub struct AntiEnumerationLayer { - pub min_ms: u64, - pub max_ms: u64, -} - -impl Layer for AntiEnumerationLayer { - type Service = AntiEnumerationService; - - fn layer(&self, inner: S) -> Self::Service { - AntiEnumerationService { - inner, - min_ms: self.min_ms, - max_ms: self.max_ms, - } - } -} - -/// The Service implementation for AntiEnumeration. -/// It wraps the inner service, records the start time, and sleeps if the -/// inner service finishes too quickly. -#[derive(Clone)] -pub struct AntiEnumerationService { - inner: S, - min_ms: u64, - max_ms: u64, -} - -impl Service for AntiEnumerationService -where - S: Service + Send + 'static, - S::Future: Send + 'static, -{ - type Response = S::Response; - type Error = S::Error; - type Future = BoxFuture<'static, Result>; - - fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { - self.inner.poll_ready(cx) - } - - fn call(&mut self, req: Request) -> Self::Future { - let start = Instant::now(); - let min = self.min_ms; - let max = self.max_ms; - let future = self.inner.call(req); - - Box::pin(async move { - let res = future.await?; - let elapsed = start.elapsed(); - - // Pick a random target within the window to make timing analysis even harder - let target = Duration::from_millis(rand::rng().random_range(min..=max)); - - if elapsed < target { - sleep(target - elapsed).await; - } - Ok(res) - }) - } -} diff --git a/src/controller/middleware/RateLimitLayer.rs b/src/controller/middleware/RateLimitLayer.rs deleted file mode 100644 index 4dbba09..0000000 --- a/src/controller/middleware/RateLimitLayer.rs +++ /dev/null @@ -1,97 +0,0 @@ -use std::task::{Context, Poll}; -use axum::{extract::Request, response::Response}; -use futures_util::future::BoxFuture; -use tower::{Layer, Service}; -use std::time::Instant; -use crate::state::AppState; -use crate::errors::AppError; - -/// Middleware Layer for Rate Limiting. -/// It implements a Token Bucket algorithm to limit requests per IP. -/// This prevents spam, brute-force attacks, and DoS on expensive endpoints. -#[derive(Clone)] -pub struct RateLimitLayer { - pub state: AppState, - pub max_tokens: f64, - pub refill_rate: f64, // tokens per second -} - -impl Layer for RateLimitLayer { - type Service = RateLimitService; - - fn layer(&self, inner: S) -> Self::Service { - RateLimitService { - inner, - state: self.state.clone(), - max_tokens: self.max_tokens, - refill_rate: self.refill_rate, - } - } -} - -/// The Service implementation for RateLimiting. -/// It identifies the user via 'X-Client-IP' header (injected by the trusted proxy). -#[derive(Clone)] -pub struct RateLimitService { - inner: S, - state: AppState, - max_tokens: f64, - refill_rate: f64, -} - -impl Service for RateLimitService -where - S: Service + Send + 'static, - S::Future: Send + 'static, -{ - type Response = S::Response; - type Error = S::Error; - type Future = BoxFuture<'static, Result>; - - fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { - self.inner.poll_ready(cx) - } - - fn call(&mut self, req: Request) -> Self::Future { - // 1. Extract IP from trusted header (X-Client-IP) - // Note: In a production Docker setup, Nginx should be configured to set this. - let client_ip = req.headers() - .get("X-Client-IP") - .and_then(|h| h.to_str().ok()) - .map(|s| s.to_string()) - .unwrap_or_else(|| "unknown".to_string()); - - let state = self.state.clone(); - let max_tokens = self.max_tokens; - let refill_rate = self.refill_rate; - - // 2. Access the shared DashMap for the IP's bucket - let mut bucket = state.rate_limit.entry(client_ip).or_insert_with(|| crate::state::TokenBucket::new(max_tokens)); - - let now = Instant::now(); - let elapsed = now.duration_since(bucket.last_refill).as_secs_f64(); - - // 3. Token Bucket Refill logic: tokens = current + (time_passed * rate) - bucket.tokens = (bucket.tokens + elapsed * refill_rate).min(max_tokens); - bucket.last_refill = now; - - // 4. Consumption check - if bucket.tokens >= 1.0 { - bucket.tokens -= 1.0; - drop(bucket); // CRITICAL: Release the lock before proceeding to allow other threads to access the map - let future = self.inner.call(req); - Box::pin(async move { - let res = future.await?; - Ok(res) - }) - } else { - drop(bucket); - // Limit exceeded: return 429 Too Many Requests - Box::pin(async move { - let err = AppError::Validation("Too many requests".to_string()); - use axum::response::IntoResponse; - Ok(err.into_response()) - }) - } - } -} diff --git a/src/controller/middleware/mod.rs b/src/controller/middleware/mod.rs deleted file mode 100644 index c48fe14..0000000 --- a/src/controller/middleware/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod AntiEnumerationLayer; -pub mod RateLimitLayer; diff --git a/src/controller/mod.rs b/src/controller/mod.rs index e76ac26..f5aea4b 100644 --- a/src/controller/mod.rs +++ b/src/controller/mod.rs @@ -3,7 +3,6 @@ use tower_http::trace::TraceLayer; use crate::state::AppState; -mod middleware; pub mod model; mod v1; diff --git a/src/controller/v1/auth_controller.rs b/src/controller/v1/auth_controller.rs index 2b7716e..bc4eaa3 100644 --- a/src/controller/v1/auth_controller.rs +++ b/src/controller/v1/auth_controller.rs @@ -3,11 +3,9 @@ use axum::{Json, Router, routing::post}; use tower_cookies::{CookieManagerLayer, Cookies}; use crate::{ - controller::middleware::AntiEnumerationLayer::AntiEnumerationLayer, - controller::middleware::RateLimitLayer::RateLimitLayer, controller::model::auth_model::{AuthResponse, LoginRequest, RegisterRequest}, errors::AppError, - service::auth_service::{login, register, refresh}, + service::auth_service::{login, refresh, register}, state::AppState, }; @@ -17,19 +15,9 @@ pub fn auth_router(state: AppState) -> Router { .route("/register", post(register_handler)) .route("/refresh", post(refresh_handler)) .route("/logout", post(logout_handler)) - .layer(AntiEnumerationLayer { - min_ms: 150, - max_ms: 300, - }) - .layer(RateLimitLayer { - state, - max_tokens: 5.0, - refill_rate: 1.0 / 12.0, // 1 token every 12 seconds = 5 per minute - }) .layer(CookieManagerLayer::new()) } - async fn login_handler( State(s): State, cookies: Cookies, @@ -52,9 +40,6 @@ async fn refresh_handler( refresh(&s, cookies).await } -async fn logout_handler( - State(s): State, - cookies: Cookies, -) -> Result<(), AppError> { +async fn logout_handler(State(s): State, cookies: Cookies) -> Result<(), AppError> { crate::service::auth_service::logout(&s, cookies).await }