14 KiB
Portal Server Security Implementation Roadmap
Overview
This roadmap outlines the prioritized implementation plan for enhancing the Portal Server's security posture. The recommendations are organized by priority and implementation complexity.
Phase 1: Critical Security Fixes (Week 1-2)
🔴 P0: Webhook Signature Verification
Status: Critical Gap
Effort: 2-3 days
Dependencies: Add hmac
and sha2
crates
Implementation Plan
-
Add Dependencies
# Cargo.toml hmac = "0.12" sha2 = "0.10" hex = "0.4"
-
Implement Stripe Webhook Verification
// src/services.rs use hmac::{Hmac, Mac}; use sha2::Sha256; impl StripeService { pub fn verify_webhook_signature(&self, payload: &str, signature: &str, webhook_secret: &str) -> bool { let elements: Vec<&str> = signature.split(',').collect(); let timestamp = elements.iter() .find(|&&x| x.starts_with("t=")) .and_then(|x| x.strip_prefix("t=")) .and_then(|x| x.parse::<i64>().ok()); let signature_hash = elements.iter() .find(|&&x| x.starts_with("v1=")) .and_then(|x| x.strip_prefix("v1=")); if let (Some(timestamp), Some(sig)) = (timestamp, signature_hash) { let signed_payload = format!("{}.{}", timestamp, payload); let mut mac = Hmac::<Sha256>::new_from_slice(webhook_secret.as_bytes()).unwrap(); mac.update(signed_payload.as_bytes()); let expected = hex::encode(mac.finalize().into_bytes()); expected == sig } else { false } } }
-
Implement Identify Webhook Verification
// src/services.rs impl IdentifyService { pub fn verify_webhook_signature(&self, payload: &str, signature: &str) -> bool { let mut mac = Hmac::<Sha256>::new_from_slice(self.webhook_secret.as_bytes()).unwrap(); mac.update(payload.as_bytes()); let expected = hex::encode(mac.finalize().into_bytes()); let provided = signature.trim_start_matches("sha256="); expected == provided } }
🔴 P0: HTTPS Enforcement
Status: Missing
Effort: 1 day
Dependencies: TLS certificate configuration
Implementation Plan
-
Add TLS Support
# Cargo.toml tokio-rustls = "0.24" rustls-pemfile = "1.0"
-
Configure HTTPS Server
// src/server.rs use tokio_rustls::{TlsAcceptor, rustls::ServerConfig as TlsConfig}; impl PortalServer { pub async fn run_with_tls(self, cert_path: &str, key_path: &str) -> Result<()> { let certs = load_certs(cert_path)?; let key = load_private_key(key_path)?; let config = TlsConfig::builder() .with_safe_defaults() .with_no_client_auth() .with_single_cert(certs, key)?; let acceptor = TlsAcceptor::from(Arc::new(config)); // Implement TLS server binding } }
Phase 2: Authentication & Authorization (Week 3-4)
🟡 P1: API Key Authentication
Status: Not Implemented
Effort: 3-4 days
Dependencies: Database for API key storage
Implementation Plan
-
Add API Key Model
// src/models.rs #[derive(Debug, Clone)] pub struct ApiKey { pub id: String, pub key_hash: String, pub name: String, pub permissions: Vec<String>, pub created_at: DateTime<Utc>, pub expires_at: Option<DateTime<Utc>>, pub last_used: Option<DateTime<Utc>>, }
-
Implement Authentication Middleware
// src/middleware/auth.rs use axum::{ extract::{Request, State}, http::{HeaderMap, StatusCode}, middleware::Next, response::Response, }; pub async fn api_key_auth( State(state): State<AppState>, headers: HeaderMap, request: Request, next: Next, ) -> Result<Response, StatusCode> { let api_key = headers .get("x-api-key") .and_then(|v| v.to_str().ok()) .ok_or(StatusCode::UNAUTHORIZED)?; if !state.validate_api_key(api_key).await { return Err(StatusCode::UNAUTHORIZED); } Ok(next.run(request).await) }
-
Protected Route Configuration
// src/server.rs let protected_routes = Router::new() .route("/api/kyc/create-verification-session", post(handlers::create_verification_session)) .route("/api/company/create-payment-intent", post(handlers::create_payment_intent)) .layer(middleware::from_fn_with_state(app_state.clone(), api_key_auth));
🟡 P1: Rate Limiting
Status: Not Implemented
Effort: 2-3 days
Dependencies: Redis for distributed rate limiting
Implementation Plan
-
Add Rate Limiting Dependencies
# Cargo.toml tower-governor = "0.0.4" redis = { version = "0.23", features = ["tokio-comp"] }
-
Implement Rate Limiting
// src/middleware/rate_limit.rs use tower_governor::{GovernorLayer, GovernorConfigBuilder}; pub fn create_rate_limiter() -> GovernorLayer<'static, (), axum::extract::ConnectInfo<SocketAddr>> { let governor_conf = GovernorConfigBuilder::default() .per_second(10) .burst_size(20) .key_extractor(|req: &axum::extract::ConnectInfo<SocketAddr>| req.0.ip()) .finish() .unwrap(); GovernorLayer::new(&governor_conf) }
Phase 3: Data Security (Week 5-6)
🟡 P1: Encrypted Database Storage
Status: Using In-Memory HashMap
Effort: 5-7 days
Dependencies: Database setup, encryption library
Implementation Plan
-
Add Database Dependencies
# Cargo.toml sqlx = { version = "0.7", features = ["runtime-tokio-rustls", "postgres", "chrono", "uuid"] } aes-gcm = "0.10"
-
Database Schema
-- migrations/001_initial.sql CREATE TABLE verification_sessions ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), session_id VARCHAR NOT NULL UNIQUE, user_id VARCHAR NOT NULL, email_encrypted BYTEA NOT NULL, status VARCHAR NOT NULL, verification_data_encrypted BYTEA, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), expires_at TIMESTAMPTZ NOT NULL ); CREATE INDEX idx_verification_sessions_user_id ON verification_sessions(user_id); CREATE INDEX idx_verification_sessions_session_id ON verification_sessions(session_id);
-
Encryption Service
// src/services/encryption.rs use aes_gcm::{Aes256Gcm, Key, Nonce, aead::{Aead, NewAead}}; pub struct EncryptionService { cipher: Aes256Gcm, } impl EncryptionService { pub fn new(key: &[u8; 32]) -> Self { let key = Key::from_slice(key); let cipher = Aes256Gcm::new(key); Self { cipher } } pub fn encrypt(&self, data: &str) -> Result<Vec<u8>, aes_gcm::Error> { let nonce = Nonce::from_slice(b"unique nonce"); // Use random nonce in production self.cipher.encrypt(nonce, data.as_bytes()) } pub fn decrypt(&self, encrypted_data: &[u8]) -> Result<String, aes_gcm::Error> { let nonce = Nonce::from_slice(b"unique nonce"); let decrypted = self.cipher.decrypt(nonce, encrypted_data)?; Ok(String::from_utf8_lossy(&decrypted).to_string()) } }
🟡 P2: Request Size Limits
Status: Not Implemented
Effort: 1 day
Dependencies: None
Implementation Plan
// src/server.rs
use tower_http::limit::RequestBodyLimitLayer;
router = router.layer(RequestBodyLimitLayer::new(1024 * 1024)); // 1MB limit
Phase 4: Security Headers & Monitoring (Week 7-8)
🟡 P2: Security Headers
Status: Not Implemented
Effort: 2 days
Dependencies: None
Implementation Plan
// src/middleware/security_headers.rs
use axum::{
http::{header, HeaderValue},
response::Response,
};
use tower_http::set_header::SetResponseHeaderLayer;
pub fn security_headers_layer() -> tower::layer::util::Stack<
SetResponseHeaderLayer<HeaderValue>,
tower::layer::util::Stack<SetResponseHeaderLayer<HeaderValue>, tower::layer::Identity>
> {
tower::ServiceBuilder::new()
.layer(SetResponseHeaderLayer::overriding(
header::X_CONTENT_TYPE_OPTIONS,
HeaderValue::from_static("nosniff"),
))
.layer(SetResponseHeaderLayer::overriding(
header::X_FRAME_OPTIONS,
HeaderValue::from_static("DENY"),
))
.layer(SetResponseHeaderLayer::overriding(
header::STRICT_TRANSPORT_SECURITY,
HeaderValue::from_static("max-age=31536000; includeSubDomains"),
))
.into_inner()
}
🟡 P2: Security Monitoring
Status: Basic Logging Only
Effort: 3-4 days
Dependencies: Prometheus, Grafana
Implementation Plan
-
Add Monitoring Dependencies
# Cargo.toml prometheus = "0.13" lazy_static = "1.4"
-
Security Metrics
// src/metrics.rs use prometheus::{Counter, Histogram, register_counter, register_histogram}; lazy_static! { pub static ref AUTH_FAILURES: Counter = register_counter!( "auth_failures_total", "Total number of authentication failures" ).unwrap(); pub static ref WEBHOOK_VERIFICATION_FAILURES: Counter = register_counter!( "webhook_verification_failures_total", "Total number of webhook verification failures" ).unwrap(); pub static ref RATE_LIMIT_VIOLATIONS: Counter = register_counter!( "rate_limit_violations_total", "Total number of rate limit violations" ).unwrap(); }
Phase 5: Compliance & Testing (Week 9-10)
🟡 P2: Security Testing Framework
Status: Not Implemented
Effort: 4-5 days
Dependencies: Testing tools
Implementation Plan
-
Add Security Testing Dependencies
# Cargo.toml [dev-dependencies] cargo-audit = "0.18" cargo-deny = "0.14"
-
Security Test Suite
// tests/security_tests.rs #[tokio::test] async fn test_cors_restrictions() { // Test CORS policy enforcement } #[tokio::test] async fn test_webhook_signature_verification() { // Test webhook signature validation } #[tokio::test] async fn test_rate_limiting() { // Test rate limiting enforcement } #[tokio::test] async fn test_input_validation() { // Test input sanitization }
-
Automated Security Scanning
# .github/workflows/security.yml name: Security Scan on: [push, pull_request] jobs: security: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Install Rust uses: actions-rs/toolchain@v1 - name: Security Audit run: cargo audit - name: Dependency Check run: cargo deny check
Implementation Timeline
gantt
title Portal Server Security Implementation
dateFormat YYYY-MM-DD
section Phase 1: Critical
Webhook Verification :crit, p1-1, 2025-06-30, 3d
HTTPS Enforcement :crit, p1-2, 2025-07-02, 1d
section Phase 2: Auth
API Key Authentication :p2-1, 2025-07-03, 4d
Rate Limiting :p2-2, 2025-07-07, 3d
section Phase 3: Data
Database Storage :p3-1, 2025-07-10, 7d
Request Limits :p3-2, 2025-07-17, 1d
section Phase 4: Headers
Security Headers :p4-1, 2025-07-18, 2d
Security Monitoring :p4-2, 2025-07-20, 4d
section Phase 5: Testing
Security Testing :p5-1, 2025-07-24, 5d
Success Criteria
Phase 1 Completion
- All webhook signatures properly verified
- HTTPS enforced in production
- No critical security vulnerabilities
Phase 2 Completion
- API key authentication implemented
- Rate limiting active on all endpoints
- Authentication bypass attempts blocked
Phase 3 Completion
- All sensitive data encrypted at rest
- Database storage implemented
- Request size limits enforced
Phase 4 Completion
- Security headers implemented
- Security metrics collection active
- Monitoring dashboards deployed
Phase 5 Completion
- Automated security testing in CI/CD
- Security documentation complete
- Penetration testing passed
Risk Mitigation
High-Risk Scenarios
- API Key Compromise: Implement key rotation, monitoring
- Database Breach: Encryption at rest, access controls
- DDoS Attack: Rate limiting, CDN protection
- Insider Threat: Audit logging, access controls
Rollback Plans
- Each phase includes rollback procedures
- Feature flags for gradual rollout
- Database migration rollback scripts
- Configuration rollback procedures
Resource Requirements
Development Resources
- Senior Security Engineer: 40 hours/week for 10 weeks
- Backend Developer: 20 hours/week for 10 weeks
- DevOps Engineer: 10 hours/week for 10 weeks
Infrastructure Requirements
- Database: PostgreSQL with encryption
- Monitoring: Prometheus + Grafana
- Security Tools: SIEM, vulnerability scanner
- Testing Environment: Isolated security testing environment
Document Version: 1.0
Last Updated: 2025-06-29
Owner: Security Team
Review Cycle: Monthly