Files
michaelschiemer/docs/livecomponents/security-guide.md
Michael Schiemer fc3d7e6357 feat(Production): Complete production deployment infrastructure
- Add comprehensive health check system with multiple endpoints
- Add Prometheus metrics endpoint
- Add production logging configurations (5 strategies)
- Add complete deployment documentation suite:
  * QUICKSTART.md - 30-minute deployment guide
  * DEPLOYMENT_CHECKLIST.md - Printable verification checklist
  * DEPLOYMENT_WORKFLOW.md - Complete deployment lifecycle
  * PRODUCTION_DEPLOYMENT.md - Comprehensive technical reference
  * production-logging.md - Logging configuration guide
  * ANSIBLE_DEPLOYMENT.md - Infrastructure as Code automation
  * README.md - Navigation hub
  * DEPLOYMENT_SUMMARY.md - Executive summary
- Add deployment scripts and automation
- Add DEPLOYMENT_PLAN.md - Concrete plan for immediate deployment
- Update README with production-ready features

All production infrastructure is now complete and ready for deployment.
2025-10-25 19:18:37 +02:00

692 lines
16 KiB
Markdown

# 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
<!-- 1. CSRF token in meta tag -->
<meta name="csrf-token" content="generated-token-here">
<!-- 2. Automatically included in requests -->
<button data-lc-action="deleteAccount">Delete Account</button>
<!-- 3. Server validates token -->
```
**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('/<script|javascript:/i', $email)) {
return true;
}
return false;
}
}
```
### XSS Protection
**Automatic HTML Escaping**:
```php
public function render(): string
{
return $this->view('component', [
'userInput' => $this->userInput // Automatically escaped
]);
}
```
**Template Escaping**:
```html
<!-- Automatic escaping -->
<div>{userInput}</div>
<!-- Raw HTML (use with extreme caution) -->
<div>{!! sanitizedHtml !!}</div>
```
**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) →