485 lines
14 KiB
Markdown
485 lines
14 KiB
Markdown
# 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
|
|
1. **Add Dependencies**
|
|
```toml
|
|
# Cargo.toml
|
|
hmac = "0.12"
|
|
sha2 = "0.10"
|
|
hex = "0.4"
|
|
```
|
|
|
|
2. **Implement Stripe Webhook Verification**
|
|
```rust
|
|
// 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
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
3. **Implement Identify Webhook Verification**
|
|
```rust
|
|
// 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
|
|
1. **Add TLS Support**
|
|
```toml
|
|
# Cargo.toml
|
|
tokio-rustls = "0.24"
|
|
rustls-pemfile = "1.0"
|
|
```
|
|
|
|
2. **Configure HTTPS Server**
|
|
```rust
|
|
// 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
|
|
1. **Add API Key Model**
|
|
```rust
|
|
// 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>>,
|
|
}
|
|
```
|
|
|
|
2. **Implement Authentication Middleware**
|
|
```rust
|
|
// 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)
|
|
}
|
|
```
|
|
|
|
3. **Protected Route Configuration**
|
|
```rust
|
|
// 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
|
|
1. **Add Rate Limiting Dependencies**
|
|
```toml
|
|
# Cargo.toml
|
|
tower-governor = "0.0.4"
|
|
redis = { version = "0.23", features = ["tokio-comp"] }
|
|
```
|
|
|
|
2. **Implement Rate Limiting**
|
|
```rust
|
|
// 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
|
|
1. **Add Database Dependencies**
|
|
```toml
|
|
# Cargo.toml
|
|
sqlx = { version = "0.7", features = ["runtime-tokio-rustls", "postgres", "chrono", "uuid"] }
|
|
aes-gcm = "0.10"
|
|
```
|
|
|
|
2. **Database Schema**
|
|
```sql
|
|
-- 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);
|
|
```
|
|
|
|
3. **Encryption Service**
|
|
```rust
|
|
// 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
|
|
```rust
|
|
// 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
|
|
```rust
|
|
// 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
|
|
1. **Add Monitoring Dependencies**
|
|
```toml
|
|
# Cargo.toml
|
|
prometheus = "0.13"
|
|
lazy_static = "1.4"
|
|
```
|
|
|
|
2. **Security Metrics**
|
|
```rust
|
|
// 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
|
|
1. **Add Security Testing Dependencies**
|
|
```toml
|
|
# Cargo.toml
|
|
[dev-dependencies]
|
|
cargo-audit = "0.18"
|
|
cargo-deny = "0.14"
|
|
```
|
|
|
|
2. **Security Test Suite**
|
|
```rust
|
|
// 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
|
|
}
|
|
```
|
|
|
|
3. **Automated Security Scanning**
|
|
```bash
|
|
# .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
|
|
|
|
```mermaid
|
|
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
|
|
1. **API Key Compromise**: Implement key rotation, monitoring
|
|
2. **Database Breach**: Encryption at rest, access controls
|
|
3. **DDoS Attack**: Rate limiting, CDN protection
|
|
4. **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 |