# LiveComponents Security Guide **Production-Ready Security for Interactive Components** This guide covers security best practices, built-in protections, and threat mitigation strategies for LiveComponents. ## Security Architecture ``` ┌─────────────────────────────────────────────────┐ │ Security Layers (Defense in Depth) │ ├─────────────────────────────────────────────────┤ │ 1. CSRF Protection (Token Validation) │ │ 2. Rate Limiting (DoS Prevention) │ │ 3. Idempotency Keys (Replay Protection) │ │ 4. Input Validation (XSS/Injection) │ │ 5. Action Authorization (Access Control) │ │ 6. State Encryption (Data Protection) │ └─────────────────────────────────────────────────┘ ``` ## 1. CSRF Protection ### Automatic CSRF Protection **All LiveComponent requests are automatically protected** against Cross-Site Request Forgery attacks. #### How It Works ```html ``` **Request Headers**: ```http POST /livecomponent/component-id/action X-CSRF-Token: generated-token-here Content-Type: application/json ``` **Server Validation**: ```php final readonly class CsrfMiddleware { public function process(Request $request, callable $next): Response { if ($this->isStateChanging($request)) { $this->validator->validate($request); } return $next($request); } } ``` ### Configuration ```env # .env LIVECOMPONENT_CSRF_PROTECTION=true # Enable/disable CSRF_TOKEN_LIFETIME=7200 # 2 hours CSRF_REGENERATE_ON_ACTION=false # Regenerate after each action ``` ### Manual CSRF Token Refresh ```javascript // Refresh CSRF token (e.g., after long session) LiveComponent.refreshCsrfToken().then(() => { console.log('CSRF token refreshed'); }); ``` ### Handling CSRF Errors ```php use App\Framework\Exception\Security\CsrfTokenMismatchException; try { $this->executeAction($component, $action); } catch (CsrfTokenMismatchException $e) { return new JsonResponse([ 'success' => false, 'error' => 'Your session has expired. Please refresh the page.', 'code' => 'CSRF_TOKEN_EXPIRED' ], Status::FORBIDDEN); } ``` **Client-Side Handling**: ```javascript window.addEventListener('livecomponent:error', (e) => { if (e.detail.code === 'CSRF_TOKEN_EXPIRED') { // Refresh token and retry LiveComponent.refreshCsrfToken().then(() => { LiveComponent.retryLastAction(); }); } }); ``` --- ## 2. Rate Limiting ### Per-Component Rate Limits Protect against abuse and DoS attacks with intelligent rate limiting. #### Component-Level Configuration ```php use App\Framework\LiveComponents\Attributes\RateLimit; #[RateLimit(requests: 10, window: 60)] // 10 requests per 60 seconds final class SearchComponent extends LiveComponent { #[LiveAction] #[RateLimit(requests: 5, window: 60)] // Stricter limit for this action public function performSearch(): void { // Expensive search operation } } ``` #### Global Configuration ```env # .env LIVECOMPONENT_RATE_LIMIT=60 # Default: 60 requests/minute LIVECOMPONENT_RATE_LIMIT_WINDOW=60 # Window in seconds LIVECOMPONENT_RATE_LIMIT_BY=ip # 'ip', 'session', or 'user' ``` ### Rate Limit Strategies **1. IP-Based** (Anonymous users): ```php $key = RateLimitKey::forIp($request->server->getRemoteAddr()); ``` **2. Session-Based** (Authenticated sessions): ```php $key = RateLimitKey::forSession($session->getId()); ``` **3. User-Based** (Logged-in users): ```php $key = RateLimitKey::forUser($currentUser->id); ``` ### Handling Rate Limit Errors ```php use App\Framework\Exception\Http\RateLimitExceededException; try { $this->executeAction($component, $action); } catch (RateLimitExceededException $e) { return new JsonResponse([ 'success' => false, 'error' => 'Too many requests. Please slow down.', 'retry_after' => $e->getRetryAfter()->toSeconds() ], Status::TOO_MANY_REQUESTS); } ``` **Client-Side**: ```javascript window.addEventListener('livecomponent:rate-limited', (e) => { const retryAfter = e.detail.retryAfter; // Show countdown to user showNotification(`Too many requests. Try again in ${retryAfter} seconds.`); // Disable actions temporarily disableActionsFor(retryAfter); }); ``` --- ## 3. Idempotency Keys Prevent duplicate submissions and ensure operations execute exactly once. ### Automatic Idempotency **Critical actions** are automatically protected with idempotency keys: ```php use App\Framework\LiveComponents\Attributes\Idempotent; final class PaymentComponent extends LiveComponent { #[LiveAction] #[Idempotent] // Ensures execute-once semantics public function processPayment(): void { $this->paymentGateway->charge($this->amount); $this->orderService->createOrder($this->cartItems); } } ``` ### How It Works ``` Client Server │ │ ├─ POST (idempotency_key: abc) ─>│ 1. Store key → Execute action │ │ ├─ POST (idempotency_key: abc) ─>│ 2. Key exists → Return cached result │<─ Cached Result ─────────────┤ (No re-execution) ``` ### Manual Idempotency Keys ```javascript // Generate client-side idempotency key const idempotencyKey = crypto.randomUUID(); LiveComponent.executeAction('component-id', 'processPayment', { amount: 99.99 }, { idempotencyKey: idempotencyKey }); ``` ### Configuration ```env LIVECOMPONENT_IDEMPOTENCY_ENABLED=true LIVECOMPONENT_IDEMPOTENCY_TTL=3600 # Cache results for 1 hour ``` --- ## 4. Input Validation ### Server-Side Validation (Required) **Never trust client input**. Always validate on the server: ```php use App\Framework\LiveComponents\Attributes\Validated; use App\Framework\Validation\Rules; final class RegistrationComponent extends LiveComponent { #[LiveProp] #[Validated([ Rules::required(), Rules::email(), Rules::maxLength(255) ])] public string $email = ''; #[LiveProp] #[Validated([ Rules::required(), Rules::minLength(8), Rules::containsUppercase(), Rules::containsNumber() ])] public string $password = ''; #[LiveAction] public function register(): void { // Validation happens automatically before action execution // If validation fails, action is not executed $this->userService->register( new Email($this->email), new Password($this->password) ); } } ``` ### Value Object Validation **Best Practice**: Use Value Objects for automatic validation: ```php 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'); } } private function containsSuspiciousPatterns(string $email): bool { // Detect SQL injection attempts if (preg_match('/[;\'"<>]/', $email)) { return true; } // Detect XSS attempts if (preg_match('/view('component', [ 'userInput' => $this->userInput // Automatically escaped ]); } ``` **Template Escaping**: ```html
{userInput}
{!! sanitizedHtml !!}
``` **Sanitization Helper**: ```php use App\Framework\Security\HtmlSanitizer; final class CommentComponent extends LiveComponent { #[LiveProp] public string $comment = ''; #[LiveAction] public function postComment(): void { // Sanitize before storage $sanitized = $this->htmlSanitizer->sanitize($this->comment); $this->commentRepository->save(new Comment($sanitized)); } } ``` ### SQL Injection Protection **Use Parameterized Queries** (framework automatic): ```php // ✅ Safe - parameterized $users = $this->db->query( 'SELECT * FROM users WHERE email = ?', [$this->email] ); // ❌ NEVER do this - vulnerable to SQL injection $users = $this->db->query( "SELECT * FROM users WHERE email = '{$this->email}'" ); ``` --- ## 5. Action Authorization ### Role-Based Access Control ```php use App\Framework\LiveComponents\Attributes\Authorize; final class AdminDashboard extends LiveComponent { #[LiveAction] #[Authorize(roles: ['admin'])] public function deleteUser(string $userId): void { $this->userService->delete($userId); } #[LiveAction] #[Authorize(permissions: ['users.edit'])] public function updateUserRole(string $userId, string $role): void { $this->userService->updateRole($userId, $role); } } ``` ### Custom Authorization ```php use App\Framework\LiveComponents\Attributes\LiveAction; final class PostEditor extends LiveComponent { #[LiveProp] public string $postId = ''; #[LiveAction] public function publish(): void { // Custom authorization logic $post = $this->postRepository->find($this->postId); if (!$this->canPublish($post)) { throw new UnauthorizedException('You cannot publish this post'); } $post->publish(); $this->postRepository->save($post); } private function canPublish(Post $post): bool { // Owner can always publish if ($post->authorId === $this->currentUser->id) { return true; } // Editors can publish if approved if ($this->currentUser->hasRole('editor') && $post->isApproved()) { return true; } return false; } } ``` --- ## 6. State Encryption ### Sensitive Data Protection **Encrypt sensitive component state** before sending to client: ```php use App\Framework\LiveComponents\Attributes\Encrypted; final class PaymentComponent extends LiveComponent { #[LiveProp] #[Encrypted] // Encrypted in client state public string $creditCardNumber = ''; #[LiveProp] #[Encrypted] public string $cvv = ''; #[LiveProp] // Not encrypted - safe to expose public string $cardholderName = ''; } ``` ### How It Works ``` Server → Encrypt → Client → Store encrypted → Send back → Server → Decrypt ``` **Never expose sensitive data in plain text to the client**. ### Configuration ```env LIVECOMPONENT_STATE_ENCRYPTION=true VAULT_ENCRYPTION_KEY=base64:your-256-bit-key-here ``` --- ## 7. Security Headers ### Automatic Security Headers Framework automatically sets security headers for LiveComponent responses: ```http X-Frame-Options: SAMEORIGIN X-Content-Type-Options: nosniff X-XSS-Protection: 1; mode=block Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline' Referrer-Policy: strict-origin-when-cross-origin ``` ### Configure CSP for LiveComponents ```php // config/security.php return [ 'csp' => [ 'default-src' => ["'self'"], 'script-src' => ["'self'", "'unsafe-inline'"], // Required for inline event handlers 'style-src' => ["'self'", "'unsafe-inline'"], // Required for scoped styles 'connect-src' => ["'self'", 'wss://yourdomain.com'], // For SSE ] ]; ``` --- ## 8. OWASP Top 10 Coverage ### A01: Broken Access Control - ✅ `#[Authorize]` attribute for actions - ✅ Custom authorization logic - ✅ Session-based authentication ### A02: Cryptographic Failures - ✅ Encrypted state for sensitive data - ✅ HTTPS enforced in production - ✅ Secure random token generation ### A03: Injection - ✅ Parameterized queries (automatic) - ✅ HTML escaping (automatic) - ✅ Value Object validation ### A04: Insecure Design - ✅ Idempotency for critical actions - ✅ Rate limiting per component - ✅ Defense in depth architecture ### A05: Security Misconfiguration - ✅ Secure defaults - ✅ Environment-based configuration - ✅ Security headers automatic ### A06: Vulnerable Components - ✅ Dependency scanning (Composer) - ✅ Regular framework updates - ✅ Minimal external dependencies ### A07: Authentication Failures - ✅ Session-based auth - ✅ CSRF protection - ✅ Rate limiting on auth actions ### A08: Software Integrity Failures - ✅ Idempotency keys - ✅ State encryption - ✅ Request signing ### A09: Logging Failures - ✅ Security event logging (OWASP) - ✅ Action audit trail - ✅ Error tracking ### A10: Server-Side Request Forgery - ✅ URL validation - ✅ Whitelist-based external requests - ✅ Network isolation --- ## Production Security Checklist ### Pre-Deployment - [ ] Enable CSRF protection (`LIVECOMPONENT_CSRF_PROTECTION=true`) - [ ] Configure rate limiting for all components - [ ] Add `#[Idempotent]` to critical actions (payments, orders) - [ ] Validate all user inputs with `#[Validated]` or Value Objects - [ ] Add `#[Authorize]` to admin/privileged actions - [ ] Encrypt sensitive state with `#[Encrypted]` - [ ] Review and test all authorization logic - [ ] Set up OWASP security event logging - [ ] Configure security headers and CSP - [ ] Enable HTTPS and HSTS headers ### Monitoring - [ ] Monitor rate limit violations - [ ] Track CSRF token errors - [ ] Alert on repeated authorization failures - [ ] Log all idempotency key violations - [ ] Track input validation failures - [ ] Monitor for unusual activity patterns ### Incident Response - [ ] Have rollback plan for compromised components - [ ] Document security event response procedures - [ ] Set up automated alerts for critical security events - [ ] Regular security audit schedule - [ ] Penetration testing for critical components --- ## Security Best Practices ### 1. Principle of Least Privilege ```php // ❌ Bad - expose too much #[LiveProp] public User $currentUser; // ✅ Good - expose only what's needed #[LiveProp] public string $currentUserName; #[LiveProp] public bool $isAdmin; ``` ### 2. Validate, Don't Filter ```php // ❌ Bad - filtering can be bypassed $email = filter_var($input, FILTER_SANITIZE_EMAIL); // ✅ Good - validate and reject invalid input if (!filter_var($input, FILTER_VALIDATE_EMAIL)) { throw new InvalidArgumentException('Invalid email'); } $email = $input; ``` ### 3. Fail Securely ```php // ❌ Bad - defaults to allowing access if ($this->currentUser->hasRole('admin') ?? true) { $this->deleteUser(); } // ✅ Good - defaults to denying access if ($this->currentUser?->hasRole('admin') === true) { $this->deleteUser(); } else { throw new UnauthorizedException(); } ``` ### 4. Never Trust Client State ```php // ❌ Bad - trust client-provided data #[LiveProp] public bool $isAdmin = false; // Client can modify this! // ✅ Good - compute from server-side source of truth public function isAdmin(): bool { return $this->currentUser?->hasRole('admin') ?? false; } ``` --- ## Reporting Security Issues **Please do not open public issues for security vulnerabilities.** Email: security@example.com Include: - Component name and version - Detailed description of vulnerability - Steps to reproduce - Potential impact assessment We aim to respond within 24 hours and provide fixes within 7 days for critical vulnerabilities. --- **Next**: [Performance Guide](performance-guide.md) →