- Remove middleware reference from Gitea Traefik labels (caused routing issues) - Optimize Gitea connection pool settings (MAX_IDLE_CONNS=30, authentication_timeout=180s) - Add explicit service reference in Traefik labels - Fix intermittent 504 timeouts by improving PostgreSQL connection handling Fixes Gitea unreachability via git.michaelschiemer.de
30 KiB
Security Patterns
Comprehensive security architecture and patterns for production deployment.
Web Application Firewall (WAF)
Overview
Multi-layer WAF system with intelligent threat detection, machine learning integration, and real-time monitoring.
Core Components:
WafEngine: Central orchestrator for all security layersWafMiddleware: HTTP middleware integrationThreatAssessmentService: Unified threat evaluationMachineLearningEngine: ML-based anomaly detection (optional)
WAF Architecture
Request → WafMiddleware → WafEngine → Security Layers → ThreatAssessment → Block/Allow
↓
[SQL Injection, XSS, Path Traversal,
Command Injection, Rate Limiting,
Suspicious User Agents]
Security Layers
1. SQL Injection Layer
Protection: Detects SQL injection attempts in query parameters, POST data, headers
Patterns: UNION SELECT, DROP TABLE, ; DELETE, ' OR 1=1, --, /**/
Severity: CRITICAL
Action: Block + Log
2. XSS (Cross-Site Scripting) Layer
Protection: Detects XSS attack vectors
Patterns: <script>, javascript:, onerror=, onload=, eval(), innerHTML
Severity: HIGH
Action: Block + Sanitize + Log
3. Path Traversal Layer
Protection: Prevents directory traversal attacks
Patterns: ../, ..\\, /etc/passwd, C:\Windows, %2e%2e
Severity: CRITICAL
Action: Block + Log
4. Command Injection Layer
Protection: Detects shell command injection
Patterns: ; ls, | cat, && rm, `whoami`, $(command)
Severity: CRITICAL
Action: Block + Log + Alert
5. Intelligent Rate Limiting Layer
Protection: Adaptive rate limiting based on behavior patterns Algorithm: Token bucket with dynamic adjustment Thresholds: Configurable per endpoint/IP/user Action: 429 Response + Temporary block
6. Suspicious User Agent Layer
Protection: Detects malicious bots and scanners Patterns: Common scanners (sqlmap, nikto, nmap), headless browsers without proper headers Severity: MEDIUM Action: CAPTCHA challenge or block
Configuration
use App\Framework\Config\WafConfig;
use App\Framework\Core\ValueObjects\Duration;
$wafConfig = new WafConfig(
enabled: true,
mode: WafMode::BLOCKING, // or MONITORING for testing
globalTimeout: Duration::fromMilliseconds(50),
enableMachineLearning: false, // Enable for advanced detection
feedbackEnabled: true // Collect feedback for tuning
);
Usage in Middleware
// Automatic integration via WafMiddleware
final readonly class WafMiddleware implements Middleware
{
public function process(
Request $request,
callable $next
): Response {
// Analyze request through all WAF layers
$decision = $this->wafEngine->analyzeRequest($request);
if ($decision->shouldBlock()) {
// Log security event
$this->owaspLogger->logSecurityEvent(
new SecurityEventType($decision->threatType),
$request
);
// Return 403 Forbidden
return new Response(
status: Status::FORBIDDEN,
body: 'Request blocked by security policy'
);
}
return $next($request);
}
}
Manual WAF Analysis
use App\Framework\Waf\WafEngine;
// Analyze specific request
$decision = $this->wafEngine->analyzeRequest($request);
if ($decision->shouldBlock()) {
// Handle threat
$threatLevel = $decision->threatAssessment->overallRisk;
$detections = $decision->threatAssessment->detections;
foreach ($detections as $detection) {
$this->logger->warning('WAF Detection', [
'type' => $detection->type,
'severity' => $detection->severity->value,
'pattern' => $detection->pattern,
'value' => $detection->value
]);
}
}
// Get layer-specific results
$layerResults = $this->wafEngine->getLayerResults();
// Health monitoring
$health = $this->wafEngine->getHealthStatus();
// Returns: ['status' => 'healthy', 'health_percentage' => 100.0, ...]
WAF Feedback System
Collect feedback on false positives/negatives for continuous improvement:
use App\Framework\Waf\Feedback\WafFeedbackIntegrator;
// Report false positive
$feedback = $this->feedbackIntegrator->reportFalsePositive(
requestId: $requestId,
detectionType: 'sql_injection',
reason: 'Legitimate query parameter',
userName: $currentUser->name
);
// Report false negative
$feedback = $this->feedbackIntegrator->reportFalseNegative(
requestId: $requestId,
attackType: 'xss',
evidence: 'Malicious script bypassed detection',
userName: $securityTeam->name
);
Performance Characteristics
- Average Latency: <5ms per request
- Max Layers Parallel: 6 layers
- Throughput: 10,000+ requests/second
- False Positive Rate: <0.1% (with ML enabled)
- Detection Rate: >99.5% (OWASP Top 10)
Monitoring & Alerts
// Performance stats
$stats = $this->wafEngine->getPerformanceStats();
foreach ($stats as $layerName => $metrics) {
$this->monitor->gauge("waf.layer.{$layerName}.duration",
$metrics['execution_duration']->toMilliseconds()
);
$this->monitor->gauge("waf.layer.{$layerName}.detections",
$metrics['detections']
);
}
// Health checks
if ($health['status'] === 'degraded') {
$this->alerting->sendAlert(
level: AlertLevel::WARNING,
message: "WAF degraded: {$health['healthy_layers']}/{$health['total_layers']} layers healthy"
);
}
OWASP Event Integration
Overview
Comprehensive security event logging based on OWASP Application Security Logging standards.
Features:
- Standardized event types (OWASP event identifiers)
- Contextual security event data
- Integration with WAF, authentication, and authorization systems
- Real-time security monitoring
Event Types
use App\Framework\Security\OWASPEventIdentifier;
// Authentication Events
OWASPEventIdentifier::AUTHN_LOGIN_SUCCESS
OWASPEventIdentifier::AUTHN_LOGIN_FAILURE
OWASPEventIdentifier::AUTHN_LOGOUT_SUCCESS
OWASPEventIdentifier::AUTHN_SESSION_EXPIRED
OWASPEventIdentifier::AUTHN_TOKEN_INVALID
// Authorization Events
OWASPEventIdentifier::AUTHZ_PERMISSION_DENIED
OWASPEventIdentifier::AUTHZ_PRIVILEGE_ESCALATION
// Input Validation Events
OWASPEventIdentifier::INPUT_VALIDATION_FAILURE
OWASPEventIdentifier::INPUT_XSS_DETECTED
OWASPEventIdentifier::INPUT_SQL_INJECTION_DETECTED
// Session Events
OWASPEventIdentifier::SESSION_HIJACK_ATTEMPT
OWASPEventIdentifier::SESSION_FIXATION_ATTEMPT
// Security Events
OWASPEventIdentifier::SECURITY_INTRUSION_DETECTED
OWASPEventIdentifier::SECURITY_MALWARE_DETECTED
Logging Security Events
use App\Framework\Security\OWASPSecurityLogger;
// Log authentication failure
$this->owaspLogger->logSecurityEvent(
new SecurityEventType(OWASPEventIdentifier::AUTHN_LOGIN_FAILURE),
request: $request,
context: [
'username' => $credentials->username,
'ip_address' => $request->server->getRemoteAddr(),
'user_agent' => $request->server->getUserAgent(),
'failure_reason' => 'Invalid credentials'
]
);
// Log intrusion detection
$this->owaspLogger->logSecurityEvent(
new SecurityEventType(OWASPEventIdentifier::SECURITY_INTRUSION_DETECTED),
request: $request,
context: [
'attack_type' => 'sql_injection',
'detected_pattern' => "' OR 1=1--",
'threat_level' => 'critical',
'waf_layer' => 'SqlInjectionLayer'
]
);
WAF-OWASP Bridge
Automatic event logging for WAF detections:
final readonly class WafOWASPEventBridge
{
public function logWafDetection(
WafDecision $decision,
Request $request
): void {
if (!$decision->shouldBlock()) {
return;
}
$eventType = $this->mapWafThreatToOWASP(
$decision->threatAssessment->primaryThreat
);
$this->owaspLogger->logSecurityEvent(
$eventType,
$request,
context: [
'threat_score' => $decision->threatAssessment->overallRisk,
'detections' => $decision->threatAssessment->detections,
'action_taken' => 'blocked'
]
);
}
}
Event Monitoring Dashboard
// Get security event statistics
$stats = $this->owaspLogger->getEventStatistics(
timeRange: Duration::fromHours(24)
);
// Returns:
[
'total_events' => 1523,
'by_severity' => [
'critical' => 5,
'high' => 23,
'medium' => 145,
'low' => 1350
],
'by_type' => [
'authn_login_failure' => 342,
'input_validation_failure' => 891,
'authz_permission_denied' => 67,
...
],
'top_ips' => [
'203.0.113.42' => 89,
'198.51.100.10' => 67,
...
]
]
CSRF Protection
Overview
Token-based CSRF protection for all state-changing operations.
Features:
- Per-session CSRF tokens
- Automatic token validation via middleware
- Framework-level integration
- Token rotation support
Configuration
// CSRF tokens are automatically generated and validated
// Configuration via CsrfMiddleware priority
use App\Framework\Http\Middlewares\CsrfMiddleware;
// Middleware automatically validates CSRF for:
// - POST, PUT, DELETE, PATCH requests
// - Excludes: GET, HEAD, OPTIONS
Token Generation
// Automatic token generation in sessions
$csrfToken = $session->getCsrfToken();
// Manual token generation
use App\Framework\Security\CsrfTokenGenerator;
$token = $this->csrfTokenGenerator->generate();
Template Integration
<!-- Automatic token injection via CsrfTokenProcessor -->
<form method="POST" action="/api/users">
{csrf_token} <!-- Automatically replaced with hidden input -->
<input type="text" name="username" />
<button type="submit">Create User</button>
</form>
<!-- Rendered as: -->
<form method="POST" action="/api/users">
<input type="hidden" name="_csrf_token" value="generated_token_here" />
<input type="text" name="username" />
<button type="submit">Create User</button>
</form>
JavaScript/AJAX Integration
// Get token from meta tag
const csrfToken = document.querySelector('meta[name="csrf-token"]').content;
// Include in requests
fetch('/api/endpoint', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-Token': csrfToken
},
body: JSON.stringify(data)
});
Excluding Routes from CSRF
use App\Framework\Attributes\Route;
use App\Framework\Http\Method;
// API endpoints with other authentication
#[Route(path: '/api/webhook', method: Method::POST)]
#[SkipCsrf] // Custom attribute to skip CSRF for this route
public function handleWebhook(Request $request): JsonResult
{
// Validate via signature/API key instead
$this->validateWebhookSignature($request);
return new JsonResult(['status' => 'processed']);
}
Error Handling
// CSRF validation failure throws exception
use App\Framework\Exception\Security\CsrfTokenMismatchException;
try {
$this->csrfValidator->validate($request);
} catch (CsrfTokenMismatchException $e) {
// Log security event
$this->owaspLogger->logSecurityEvent(
new SecurityEventType(OWASPEventIdentifier::CSRF_ATTACK_DETECTED),
$request
);
// Return 403 Forbidden
return new Response(
status: Status::FORBIDDEN,
body: 'CSRF token validation failed'
);
}
Rate Limiting
Overview
Multi-level rate limiting with intelligent adaptation and DDoS protection.
Features:
- IP-based rate limiting
- User-based rate limiting
- Endpoint-specific limits
- Adaptive thresholds
- Distributed rate limiting support
Configuration
# Rate Limiting Configuration (.env)
RATE_LIMIT_DEFAULT=60 # Requests per window
RATE_LIMIT_WINDOW=60 # Window in seconds
RATE_LIMIT_AUTH=10 # Auth endpoint limit
RATE_LIMIT_AUTH_WINDOW=300 # Auth window (5 min)
RATE_LIMIT_API=30 # API endpoint limit
RATE_LIMIT_API_WINDOW=60 # API window
Usage Patterns
use App\Framework\RateLimit\RateLimiter;
use App\Framework\RateLimit\RateLimitKey;
// IP-based rate limiting
$key = RateLimitKey::forIp($request->server->getRemoteAddr());
$limit = RateLimit::create(requests: 60, window: Duration::fromSeconds(60));
if (!$this->rateLimiter->allow($key, $limit)) {
throw new RateLimitExceededException(
retryAfter: $this->rateLimiter->retryAfter($key)
);
}
// User-based rate limiting
$key = RateLimitKey::forUser($userId);
$limit = RateLimit::create(requests: 100, window: Duration::fromMinutes(1));
if (!$this->rateLimiter->allow($key, $limit)) {
return new JsonResponse(
data: ['error' => 'Too many requests'],
status: Status::TOO_MANY_REQUESTS,
headers: [
'Retry-After' => (string) $this->rateLimiter->retryAfter($key)->toSeconds()
]
);
}
Middleware Integration
use App\Framework\Http\Middlewares\RateLimitMiddleware;
// Automatically applied via middleware stack
// Custom limits per route:
#[Route(path: '/api/search', method: Method::GET)]
#[RateLimit(requests: 10, window: 60)] // 10 requests per minute
public function search(Request $request): JsonResult
{
return new JsonResult($this->searchService->search($request));
}
Adaptive Rate Limiting
The IntelligentRateLimitLayer in WAF automatically adjusts based on:
- Historical traffic patterns
- Attack detection
- Resource utilization
- User reputation scores
Monitoring
// Get rate limit statistics
$stats = $this->rateLimiter->getStatistics();
// Returns:
[
'total_requests' => 15234,
'blocked_requests' => 523,
'top_limited_ips' => [
'203.0.113.42' => 89,
'198.51.100.10' => 67,
],
'average_retry_after' => Duration::fromSeconds(45)
]
Authentication & Authorization
IP-based Authentication
use App\Framework\Attributes\Route;
use App\Framework\Attributes\Auth;
// Admin routes restricted to whitelisted IPs
#[Route(path: '/admin/dashboard', method: Method::GET)]
#[Auth(strategy: 'ip', allowedIps: ['127.0.0.1', '::1', '203.0.113.0/24'])]
public function dashboard(): ViewResult
{
return new ViewResult('admin/dashboard');
}
Session-based Authentication
#[Route(path: '/api/users', method: Method::POST)]
#[Auth(strategy: 'session', roles: ['admin'])]
public function createUser(CreateUserRequest $request): JsonResult
{
// Only authenticated admins can access
return new JsonResult($this->userService->create($request));
}
Token-based Authentication
#[Route(path: '/api/v1/data', method: Method::GET)]
#[Auth(strategy: 'token', scopes: ['read:data'])]
public function getData(Request $request): JsonResult
{
// Validate Bearer token
$token = $request->headers->getAuthorization();
$user = $this->tokenAuthenticator->authenticate($token);
return new JsonResult($this->dataService->getData($user));
}
Security Headers
Automatic Security Headers
Framework automatically sets security headers via middleware:
// SecurityHeadersMiddleware
$headers = [
'X-Frame-Options' => 'SAMEORIGIN',
'X-Content-Type-Options' => 'nosniff',
'X-XSS-Protection' => '1; mode=block',
'Strict-Transport-Security' => 'max-age=31536000; includeSubDomains',
'Content-Security-Policy' => $this->generateCspHeader(),
'Referrer-Policy' => 'strict-origin-when-cross-origin',
'Permissions-Policy' => 'geolocation=(), microphone=(), camera=()'
];
Content Security Policy (CSP)
// Configure CSP via environment or config
$csp = ContentSecurityPolicy::build()
->defaultSrc(["'self'"])
->scriptSrc(["'self'", "'unsafe-inline'", 'https://cdn.example.com'])
->styleSrc(["'self'", "'unsafe-inline'"])
->imgSrc(["'self'", 'data:', 'https:'])
->connectSrc(["'self'", 'wss://localhost'])
->fontSrc(["'self'", 'https://fonts.gstatic.com'])
->reportUri('/csp-report');
State Encryption for LiveComponents
Overview
Sensitive LiveComponent state data is automatically encrypted using authenticated encryption (AES-256-GCM via libsodium) to protect against tampering and unauthorized access.
Core Components:
StateEncryptor: Low-level encryption/decryption with libsodiumEncryptedStateSerializer: State serialization with automatic encryptionEncryptionTransformer: Transparent encryption in state management pipelineCacheBasedStateManager: State persistence with encryption support
Security Features
Authenticated Encryption:
- Algorithm: XSalsa20-Poly1305 (via
sodium_crypto_secretbox) - Key Size: 256-bit (32 bytes)
- Nonce: 24 bytes, unique per encryption operation
- MAC: Poly1305 authentication tag included
- Version Byte: Prepended for future compatibility
Protection Against:
- State tampering (MAC verification fails)
- Replay attacks (unique nonces per encryption)
- Unauthorized decryption (strong encryption key required)
- Side-channel attacks (constant-time comparisons)
Configuration
// Generate encryption key (only once, store in Vault)
use App\Framework\Random\SecureRandomGenerator;
$random = new SecureRandomGenerator();
$encryptionKey = $random->bytes(SODIUM_CRYPTO_SECRETBOX_KEYBYTES); // 32 bytes
// Store in Vault
$vault->store('state_encryption_key', $encryptionKey);
Environment Configuration:
# .env
VAULT_ENCRYPTION_KEY=<base64-encoded-key>
STATE_ENCRYPTION_ENABLED=true
Basic Usage
Direct Encryption
use App\Framework\LiveComponents\Serialization\StateEncryptor;
use App\Framework\Cryptography\CryptographicUtilities;
use App\Framework\Random\SecureRandomGenerator;
// Initialize encryptor
$random = new SecureRandomGenerator();
$crypto = new CryptographicUtilities($random);
$encryptionKey = $vault->get('state_encryption_key');
$encryptor = new StateEncryptor($encryptionKey, $crypto, $random);
// Encrypt plaintext
$encrypted = $encryptor->encrypt('sensitive data');
// Decrypt
$plaintext = $encryptor->decrypt($encrypted);
// Verify encryption format
if ($encryptor->isEncrypted($encrypted)) {
// Data is encrypted
}
State Serialization with Encryption
use App\Framework\LiveComponents\Serialization\EncryptedStateSerializer;
// State must implement SerializableState interface
final readonly class UserProfileState implements SerializableState
{
public function __construct(
public string $userId,
public Email $email,
public string $sessionToken // Sensitive data
) {}
public function toArray(): array
{
return [
'user_id' => $this->userId,
'email' => $this->email->value,
'session_token' => $this->sessionToken,
];
}
public static function fromArray(array $data): self
{
return new self(
userId: $data['user_id'],
email: new Email($data['email']),
sessionToken: $data['session_token']
);
}
}
// Serialize and encrypt
$serializer = new EncryptedStateSerializer($encryptor);
$encrypted = $serializer->serialize($userProfileState);
// Deserialize and decrypt
$decrypted = $serializer->deserialize($encrypted, UserProfileState::class);
Automatic Encryption via Transformer Pipeline
use App\Framework\StateManagement\CacheBasedStateManager;
use App\Framework\StateManagement\EncryptionTransformer;
// Setup state manager with encryption transformer
$encryptionTransformer = new EncryptionTransformer($serializer);
$stateManager = new CacheBasedStateManager(
$cache,
transformers: [$encryptionTransformer] // Add to transformer pipeline
);
// State is automatically encrypted when stored
$stateManager->store($componentId, $state);
// State is automatically decrypted when retrieved
$state = $stateManager->retrieve($componentId, UserProfileState::class);
Encryption Details
Encrypted Data Format:
[Version Byte: 1 byte][Nonce: 24 bytes][Ciphertext + MAC: variable]
↑ Output from sodium_crypto_secretbox
Data Flow:
1. State Object → toArray()
2. Array → JSON encoding
3. JSON → Encryption with unique nonce
4. Encrypted bytes → Base64 encoding
5. Base64 string → Cache storage
Decryption Flow:
1. Base64 string → Base64 decoding
2. Encrypted bytes → Extract version byte + nonce + ciphertext
3. Ciphertext → Decryption with MAC verification
4. Decrypted JSON → JSON decoding
5. Array → fromArray() → State Object
Key Management
Best Practices:
- Key Generation:
// Use framework's SecureRandomGenerator
$random = new SecureRandomGenerator();
$key = $random->bytes(SODIUM_CRYPTO_SECRETBOX_KEYBYTES);
// Verify key strength
$crypto = new CryptographicUtilities($random);
if (!$crypto->validateEntropy($key, 4.0)) {
throw new \RuntimeException('Weak encryption key');
}
- Key Storage:
// Store in Vault (encrypted at rest)
$vault->store('state_encryption_key', $key);
// DO NOT store in:
// - .env files (committed to git)
// - Plain text files
// - Database without encryption
- Key Rotation:
// Generate new key
$newKey = $random->bytes(SODIUM_CRYPTO_SECRETBOX_KEYBYTES);
// Re-encrypt existing states
foreach ($stateManager->getAllStates() as $id => $state) {
// Decrypt with old key
$decrypted = $oldEncryptor->decrypt($state);
// Encrypt with new key
$reencrypted = $newEncryptor->encrypt($decrypted);
// Store updated state
$stateManager->update($id, $reencrypted);
}
// Replace key in vault
$vault->store('state_encryption_key', $newKey);
Error Handling
use App\Framework\LiveComponents\Exceptions\StateEncryptionException;
try {
$encrypted = $serializer->serialize($state);
} catch (StateEncryptionException $e) {
// Encryption failed
// Possible causes:
// - Invalid encryption key
// - State serialization error
// - libsodium extension not loaded
error_log("State encryption failed: " . $e->getMessage());
throw $e;
}
try {
$state = $serializer->deserialize($encrypted, StateClass::class);
} catch (StateEncryptionException $e) {
// Decryption failed
// Possible causes:
// - Invalid/corrupted encrypted data
// - MAC verification failed (tampering detected)
// - Wrong encryption key
// - Invalid state class
error_log("State decryption failed: " . $e->getMessage());
// Security response: Do NOT expose details to client
throw new \RuntimeException('State decryption failed');
}
Security Considerations
1. Key Protection:
- NEVER commit encryption keys to version control
- Use Vault for secure key storage
- Rotate keys regularly (recommended: quarterly)
- Different keys per environment (dev/staging/prod)
2. Nonce Uniqueness:
- Framework automatically generates unique nonce per encryption
- NEVER reuse nonces with same key
- Nonce is prepended to ciphertext (no separate storage needed)
3. MAC Verification:
- Always performed during decryption
- Constant-time comparison prevents timing attacks
- Tampering detection is automatic
4. State Validation:
// Always validate decrypted state
final readonly class SecureStateManager
{
public function retrieve(string $id, string $stateClass): object
{
// 1. Retrieve encrypted state from cache
$encrypted = $this->cache->get($id);
// 2. Decrypt and deserialize
$state = $this->serializer->deserialize($encrypted, $stateClass);
// 3. Validate decrypted state
if (!$this->validator->isValid($state)) {
throw new StateValidationException('Invalid state after decryption');
}
return $state;
}
}
5. Performance Considerations:
- Encryption overhead: ~1-2ms per operation
- Use for sensitive data only
- Consider caching decrypted states in memory for short periods
- Monitor cache TTL to balance security and performance
Testing
// Example unit test
it('encrypts and decrypts state correctly', function () {
$random = new SecureRandomGenerator();
$crypto = new CryptographicUtilities($random);
$key = $random->bytes(SODIUM_CRYPTO_SECRETBOX_KEYBYTES);
$encryptor = new StateEncryptor($key, $crypto, $random);
$serializer = new EncryptedStateSerializer($encryptor);
$state = new UserProfileState(
userId: 'user123',
email: new Email('test@example.com'),
sessionToken: 'secret-token'
);
// Encrypt
$encrypted = $serializer->serialize($state);
expect($encrypted)->toBeString();
expect($encrypted)->not->toContain('secret-token'); // Plaintext not visible
// Decrypt
$decrypted = $serializer->deserialize($encrypted, UserProfileState::class);
expect($decrypted->sessionToken)->toBe('secret-token');
});
it('detects tampering via MAC verification', function () {
$encrypted = $serializer->serialize($state);
// Tamper with ciphertext
$tampered = substr($encrypted, 0, -5) . 'XXXXX';
// Should throw on decryption
$serializer->deserialize($tampered, UserProfileState::class);
})->throws(StateEncryptionException::class, 'MAC verification failed');
Migration Guide
Adding Encryption to Existing State:
// Step 1: Add EncryptionTransformer to StateManager
$encryptionTransformer = new EncryptionTransformer($serializer);
$stateManager = new CacheBasedStateManager(
$cache,
transformers: [$encryptionTransformer] // Add transformer
);
// Step 2: Existing states will be re-encrypted on next update
// No migration script needed - encryption happens transparently
// Step 3: Monitor for encryption errors
try {
$state = $stateManager->retrieve($id, StateClass::class);
} catch (StateEncryptionException $e) {
// Old unencrypted state or corruption
// Regenerate state from source
}
Input Validation
Request Validation
final readonly class CreateUserRequest implements ControllerRequest
{
public function __construct(
public Email $email, // Validated Email value object
public UserName $userName, // Validated UserName value object
public ?string $company = null
) {}
public static function fromHttpRequest(HttpRequest $request): self
{
$data = $request->parsedBody->toArray();
// Automatic validation via Value Objects
return new self(
email: new Email($data['email'] ?? ''), // Throws if invalid
userName: new UserName($data['username'] ?? ''), // Validates format
company: !empty($data['company']) ? trim($data['company']) : null
);
}
}
Value Object Validation
// Email value object with built-in validation
final readonly class Email
{
public function __construct(public string $value)
{
if (!filter_var($value, FILTER_VALIDATE_EMAIL)) {
throw new InvalidArgumentException('Invalid email format');
}
// Additional security checks
if ($this->containsSuspiciousPatterns($value)) {
throw new SecurityException('Email contains suspicious patterns');
}
}
}
Security Best Practices
1. Never Trust User Input
- Validate all input via Value Objects
- Use parameterized queries (never string concatenation)
- Sanitize output based on context (HTML, JavaScript, SQL, etc.)
2. Defense in Depth
- Multiple security layers (WAF + validation + authorization)
- Fail securely (block by default, allow explicitly)
- Log all security events
3. Secure Defaults
- HTTPS enforced in production
- Security headers enabled by default
- CSRF protection automatic
- Rate limiting active
4. Secrets Management
- Use Vault for sensitive data
- Never commit secrets to version control
- Rotate keys regularly
- Use different keys per environment
5. Monitoring & Alerting
- Monitor OWASP security events
- Alert on unusual patterns
- Regular security audits
- Automated vulnerability scanning
6. Incident Response
- Defined escalation procedures
- Security event playbooks
- Regular incident response drills
- Post-mortem analysis
Production Deployment Checklist
Encryption & Key Management
- Generate new VAULT_ENCRYPTION_KEY
- Generate STATE_ENCRYPTION_KEY and store in Vault
- Verify libsodium extension is installed (
php -m | grep sodium) - Test encryption key strength (minimum 4.0 bits/byte entropy)
- Configure key rotation schedule (recommended: quarterly)
- Set up separate encryption keys per environment
State Encryption
- Enable STATE_ENCRYPTION_ENABLED=true in production
- Add EncryptionTransformer to StateManager pipeline
- Test state encryption/decryption flow
- Verify encrypted states in cache (no plaintext visible)
- Monitor encryption performance (<2ms overhead)
- Set up alerts for StateEncryptionException errors
WAF & Security Headers
- Configure ADMIN_ALLOWED_IPS with production IPs
- Enable WAF in BLOCKING mode
- Configure rate limits for production traffic
- Set up OWASP event monitoring
- Enable security headers
- Configure CSP policy
- Test CSRF protection
SSL/TLS & Network Security
- Verify SSL/TLS configuration
- Review and update firewall rules
- Test HTTPS enforcement
- Configure HSTS headers
Monitoring & Alerting
- Set up security alerting
- Configure StateEncryptionException monitoring
- Monitor encryption key usage patterns
- Set up MAC verification failure alerts
- Test incident response procedures
Security Testing
- Conduct security penetration test
- Test state tampering detection (MAC verification)
- Verify encrypted state cannot be decrypted without key
- Test key rotation process
- Validate error handling doesn't leak sensitive info
Security Contacts
Security Issues: Report to security@example.com Bug Bounty: https://example.com/security/bug-bounty Security Documentation: This file and related guides