Implement rate limiting middleware for authentication endpoints, adding security headers middleware, and comprehensive error handling. Enhance validation tests for various DTOs and ensure proper functionality of gacha credits and rolls. Add unit tests for rate limiting and security headers middleware to validate behavior under different conditions.
This commit is contained in:
@@ -1,7 +1,11 @@
|
||||
pub mod auth_middleware;
|
||||
pub mod cors_middleware;
|
||||
pub mod permissions_middleware;
|
||||
pub mod rate_limiting_middleware;
|
||||
pub mod security_headers_middleware;
|
||||
|
||||
pub use auth_middleware::auth_middleware;
|
||||
pub use cors_middleware::cors_middleware;
|
||||
pub use permissions_middleware::PermissionsMiddlewareLayer;
|
||||
pub use rate_limiting_middleware::auth_rate_limiting_middleware;
|
||||
pub use security_headers_middleware::security_headers_middleware;
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
use axum::{
|
||||
http::{Request, StatusCode},
|
||||
middleware::Next,
|
||||
response::Response,
|
||||
Extension,
|
||||
};
|
||||
use imphnen_libs::AppState;
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
sync::{Arc, RwLock},
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
// Simple rate limiting middleware for auth endpoints
|
||||
pub async fn auth_rate_limiting_middleware(
|
||||
Extension(_state): Extension<AppState>,
|
||||
mut req: Request<axum::body::Body>,
|
||||
next: Next,
|
||||
) -> Result<Response, StatusCode> {
|
||||
let uri = req.uri().path().to_string();
|
||||
|
||||
// Only apply rate limiting to auth endpoints
|
||||
if uri == "/v1/auth/login" || uri == "/v1/auth/register" {
|
||||
// Get client IP (simplified for this example)
|
||||
let client_ip = "127.0.0.1"; // In production, use proper IP extraction
|
||||
|
||||
// Create a simple in-memory rate limiter
|
||||
let limiter = Arc::new(RwLock::new(HashMap::new()));
|
||||
|
||||
let now = Instant::now();
|
||||
let window = Duration::from_secs(60); // 1 minute window
|
||||
let max_requests = 10; // 10 requests per minute
|
||||
|
||||
{
|
||||
let mut limiter = limiter.write().unwrap();
|
||||
|
||||
// Clean up old entries
|
||||
limiter.retain(|_, (timestamp, _)| {
|
||||
now.duration_since(*timestamp) < window
|
||||
});
|
||||
|
||||
// Check rate limit
|
||||
let entry = limiter.entry(client_ip.to_string()).or_insert((now, 0));
|
||||
let (timestamp, count) = entry;
|
||||
|
||||
if now.duration_since(*timestamp) > window {
|
||||
*count = 1;
|
||||
} else if *count >= max_requests {
|
||||
return Ok(Response::builder()
|
||||
.status(StatusCode::TOO_MANY_REQUESTS)
|
||||
.header("Retry-After", "60")
|
||||
.body("Too Many Requests: Rate limit exceeded for authentication endpoint".into())
|
||||
.unwrap());
|
||||
} else {
|
||||
*count += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(next.run(req).await)
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
use axum::{
|
||||
http::{HeaderValue, Request, Response},
|
||||
middleware::Next,
|
||||
Extension,
|
||||
};
|
||||
use imphnen_libs::{AppState, ENV};
|
||||
use std::convert::Infallible;
|
||||
|
||||
/// Security headers middleware that adds various security-related HTTP headers to all responses.
|
||||
///
|
||||
/// This middleware implements security best practices by adding headers that help protect
|
||||
/// against common web attacks like clickjacking, XSS, and information leakage.
|
||||
pub async fn security_headers_middleware(
|
||||
Extension(_state): Extension<AppState>,
|
||||
mut req: Request<axum::body::Body>,
|
||||
next: Next,
|
||||
) -> Result<Response<axum::body::Body>, Infallible> {
|
||||
let res = next.run(req).await;
|
||||
|
||||
let mut res = add_security_headers(res);
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
/// Adds security headers to a response based on the current environment.
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `res` - The response to add headers to
|
||||
///
|
||||
/// # Returns
|
||||
/// The response with security headers added
|
||||
fn add_security_headers(mut res: Response<axum::body::Body>) -> Response<axum::body::Body> {
|
||||
let headers = res.headers_mut();
|
||||
|
||||
// Strict-Transport-Security (HSTS)
|
||||
// Prevents downgrade attacks and cookie hijacking
|
||||
// Only enable in production to avoid HSTS pinning issues during development
|
||||
if ENV.rust_env == "production" {
|
||||
headers.insert(
|
||||
"Strict-Transport-Security",
|
||||
HeaderValue::from_static("max-age=31536000; includeSubDomains; preload"),
|
||||
);
|
||||
} else {
|
||||
headers.insert(
|
||||
"Strict-Transport-Security",
|
||||
HeaderValue::from_static("max-age=0"),
|
||||
);
|
||||
}
|
||||
|
||||
// Content-Security-Policy (CSP)
|
||||
// Mitigates XSS and data injection attacks
|
||||
let csp = if ENV.rust_env == "production" {
|
||||
// Production CSP - strict policy for production
|
||||
"default-src 'self'; script-src 'self' https://trusted-cdn.com; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com; img-src 'self' data: https://images.example.com; connect-src 'self' https://api.example.com; frame-src 'none'; object-src 'none'; base-uri 'self'; form-action 'self'; report-uri /csp-violation-report-endpoint"
|
||||
} else {
|
||||
// Development CSP - more permissive for development
|
||||
"default-src 'self' http://localhost:3000; script-src 'self' 'unsafe-eval' 'unsafe-inline' http://localhost:3000; style-src 'self' 'unsafe-inline' http://localhost:3000; img-src 'self' data: http://localhost:3000; connect-src 'self' http://localhost:3000; frame-src 'none'; object-src 'none'; base-uri 'self'; form-action 'self'"
|
||||
};
|
||||
|
||||
headers.insert("Content-Security-Policy", HeaderValue::from_str(csp).unwrap());
|
||||
|
||||
// X-Frame-Options
|
||||
// Prevents clickjacking attacks
|
||||
headers.insert(
|
||||
"X-Frame-Options",
|
||||
HeaderValue::from_static("DENY"),
|
||||
);
|
||||
|
||||
// X-Content-Type-Options
|
||||
// Prevents MIME sniffing attacks
|
||||
headers.insert(
|
||||
"X-Content-Type-Options",
|
||||
HeaderValue::from_static("nosniff"),
|
||||
);
|
||||
|
||||
// Referrer-Policy
|
||||
// Controls how much referrer information should be included with requests
|
||||
headers.insert(
|
||||
"Referrer-Policy",
|
||||
HeaderValue::from_static("strict-origin-when-cross-origin"),
|
||||
);
|
||||
|
||||
// Permissions-Policy (Feature Policy)
|
||||
// Controls which features and APIs can be used
|
||||
headers.insert(
|
||||
"Permissions-Policy",
|
||||
HeaderValue::from_static("camera=(), microphone=(), geolocation=()"),
|
||||
);
|
||||
|
||||
// X-XSS-Protection
|
||||
// Provides basic XSS protection (note: this is a legacy header and CSP is preferred)
|
||||
headers.insert(
|
||||
"X-XSS-Protection",
|
||||
HeaderValue::from_static("1; mode=block"),
|
||||
);
|
||||
|
||||
res
|
||||
}
|
||||
Reference in New Issue
Block a user