# 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('/