🔒 Implement comprehensive security and DDoS protection

### Security Features:
- **Rate Limiting**: Redis-based IP tracking with configurable limits
  - General: 100 requests/minute (5min block)
  - Upload: 10 requests/5min (10min block)
  - Auth: 20 requests/15min (30min block)
- **Request Validation**: Path length, header count, suspicious patterns
- **Attack Detection**: Admin paths, script injections, bot patterns
- **Enhanced JWT**: Format validation, length checks, character filtering
- **IP Tracking**: X-Forwarded-For and X-Real-IP support

### Security Headers:
- X-Content-Type-Options: nosniff
- X-Frame-Options: DENY
- X-XSS-Protection: 1; mode=block
- Content-Security-Policy with strict rules
- Strict-Transport-Security with includeSubDomains

### CORS Hardening:
- Limited to specific domains: discours.io, new.discours.io
- Restricted methods: GET, POST, OPTIONS only
- Essential headers only

### Infrastructure:
- Security middleware for all requests
- Local cache + Redis for performance
- Comprehensive logging and monitoring
- Progressive blocking for repeat offenders

### Documentation:
- Complete security guide (docs/security.md)
- Configuration examples
- Incident response procedures
- Monitoring recommendations

Version bump to 0.6.0 for major security enhancement.
This commit is contained in:
2025-09-02 11:40:43 +03:00
parent d3bee5144f
commit 82668768d0
14 changed files with 803 additions and 124 deletions

View File

@@ -3,10 +3,11 @@ use actix_web::{HttpRequest, HttpResponse, Result, web};
use log::{error, info, warn};
use crate::app_state::AppState;
use crate::auth::{extract_user_id_from_token, user_added_file, validate_token};
use crate::auth::{extract_user_id_from_token, user_added_file};
use crate::handlers::MAX_USER_QUOTA_BYTES;
use crate::lookup::store_file_info;
use crate::s3_utils::{self, generate_key_with_extension, upload_to_s3};
use super::common::extract_and_validate_token;
use futures::TryStreamExt;
// use crate::thumbnail::convert_heic_to_jpeg;
@@ -19,28 +20,8 @@ pub async fn upload_handler(
mut payload: Multipart,
state: web::Data<AppState>,
) -> Result<HttpResponse, actix_web::Error> {
// Получаем токен из заголовка авторизации
let token = req
.headers()
.get("Authorization")
.and_then(|header_value| header_value.to_str().ok());
if token.is_none() {
warn!("Upload attempt without authorization token");
return Err(actix_web::error::ErrorUnauthorized(
"Authorization token required",
));
}
let token = token.unwrap();
// Сначала валидируем токен
if !validate_token(token).unwrap_or(false) {
warn!("Token validation failed");
return Err(actix_web::error::ErrorUnauthorized(
"Invalid or expired token",
));
}
// Извлекаем и валидируем токен
let token = extract_and_validate_token(&req)?;
// Затем извлекаем user_id
let user_id = extract_user_id_from_token(token).map_err(|e| {