Files
michaelschiemer/docs/livecomponents/livecomponent-security.md
Michael Schiemer 36ef2a1e2c
Some checks failed
🚀 Build & Deploy Image / Determine Build Necessity (push) Failing after 10m14s
🚀 Build & Deploy Image / Build Runtime Base Image (push) Has been skipped
🚀 Build & Deploy Image / Build Docker Image (push) Has been skipped
🚀 Build & Deploy Image / Run Tests & Quality Checks (push) Has been skipped
🚀 Build & Deploy Image / Auto-deploy to Staging (push) Has been skipped
🚀 Build & Deploy Image / Auto-deploy to Production (push) Has been skipped
Security Vulnerability Scan / Check for Dependency Changes (push) Failing after 11m25s
Security Vulnerability Scan / Composer Security Audit (push) Has been cancelled
fix: Gitea Traefik routing and connection pool optimization
- 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
2025-11-09 14:46:15 +01:00

7.3 KiB

LiveComponent Security Model

CSRF-Schutz für LiveComponents

Frage: Sollten wir CSRF grundsätzlich für LiveComponents deaktivieren?

Antwort: Ja, aber mit alternativen Sicherheitsmaßnahmen.

Warum CSRF-Deaktivierung bei LiveComponents sinnvoll ist

1. Technische Inkompatibilität

  • LiveComponents senden State per JSON, nicht als Form-Data
  • Traditionelle CSRF-Tokens in <form>-Elementen funktionieren nicht
  • AJAX-Requests benötigen andere Token-Delivery-Mechanismen
  • Token-Rotation würde LiveComponent-State invalidieren

2. Architekturelle Gründe

  • Stateless Component Model: Jeder Request enthält vollständigen State
  • Component-ID als Identifier: Komponenten sind durch eindeutige IDs identifiziert
  • Action-basierte Security: Actions werden explizit auf Component-Ebene validiert
  • Version Tracking: Concurrent Update Detection durch Version-Nummern

3. Alternative Sicherheitsmaßnahmen

LiveComponents haben ein eigenes Sicherheitsmodell:

// ComponentAction mit Validierung
final readonly class ComponentAction
{
    public function __construct(
        public string $componentId,     // Eindeutige Component-ID
        public string $method,          // Explizite Action-Methode
        public array $params,           // Validierte Parameter
        public int $version            // Concurrent Update Detection
    ) {}
}

Implementierte Security-Layer für LiveComponents

1. Origin Validation

// SameSite Cookies + Origin Header Check
if ($request->headers->get('Origin') !== $expectedOrigin) {
    throw new SecurityException('Invalid origin');
}

2. X-Requested-With Header

// AJAX-Request Verification
if ($request->headers->get('X-Requested-With') !== 'XMLHttpRequest') {
    throw new SecurityException('Invalid request type');
}

3. Component State Integrity

// State Tampering Detection
$hash = hash_hmac('sha256', json_encode($state), $secretKey);
if (!hash_equals($hash, $providedHash)) {
    throw new SecurityException('State tampering detected');
}

4. Version-based Concurrency Control

// Prevent Concurrent Update Issues
if ($currentVersion !== $expectedVersion) {
    throw new ConcurrentUpdateException('State has changed');
}

Middleware-Konfiguration

CSRF-Middleware Skip

// src/Framework/Http/Middlewares/CsrfMiddleware.php

// Skip CSRF validation for API routes and LiveComponent AJAX endpoints
// LiveComponents use stateless, component-scoped security model instead
if (str_starts_with($request->path, '/api/') ||
    str_starts_with($request->path, '/live-component/') ||
    str_starts_with($request->path, '/livecomponent/')) {
    return $next($context);
}

Honeypot-Middleware Skip

// src/Framework/Http/Middlewares/HoneypotMiddleware.php

// Skip honeypot validation for API routes and LiveComponent AJAX endpoints
if (str_starts_with($request->path, '/api/') ||
    str_starts_with($request->path, '/live-component/') ||
    str_starts_with($request->path, '/livecomponent/')) {
    return;
}

LiveComponent Routes

// Framework Route
#[Route('/live-component/{id}', method: Method::POST)]
public function handleAction(string $id, HttpRequest $request): JsonResult

// Upload Route
#[Route('/live-component/{id}/upload', method: Method::POST)]
public function handleUpload(string $id, HttpRequest $request): JsonResult

Sicherheitsempfehlungen

DO (Implementiert):

  1. Origin Validation: Same-Origin-Policy durchsetzen
  2. X-Requested-With Header: AJAX-Requests validieren
  3. Component State Integrity: State-Hashing implementieren
  4. Version Control: Concurrent Updates erkennen
  5. Rate Limiting: API-Rate-Limits für LiveComponent-Endpoints
  6. Session Validation: Authentifizierte User-Sessions prüfen

DON'T:

  1. Keine traditionellen CSRF-Tokens in LiveComponent-Requests
  2. Keine Honeypot-Felder in JSON-Payloads
  3. Keine Token-Rotation während LiveComponent-Sessions
  4. Keine Form-basierte Validierung für AJAX-Endpoints

Security Threat Model

Bedrohungen die WEITERHIN abgewehrt werden:

  • Session Hijacking: Session-Cookie mit HttpOnly + Secure Flags
  • XSS Attacks: Content Security Policy + Output Escaping
  • Man-in-the-Middle: HTTPS-Only Communication
  • Replay Attacks: Version-based Concurrency Detection
  • State Tampering: HMAC State Integrity Validation

Bedrohungen die durch CSRF-Skip entstehen könnten:

  • ⚠️ Cross-Site Request Forgery: Durch Origin Validation abgedeckt
  • ⚠️ Clickjacking: Durch X-Frame-Options Header abgedeckt
  • ⚠️ JSON Hijacking: Durch X-Requested-With Header abgedeckt

Alternative Security Implementation

Für kritische Actions (z.B. Zahlungen, Account-Löschung):

// Zusätzliche Action-Level Security
final class CriticalAction extends LiveComponent
{
    public function deleteAccount(array $params): ComponentUpdate
    {
        // 1. Re-Authentication Check
        if (!$this->session->recentlyAuthenticated()) {
            throw new ReAuthenticationRequired();
        }

        // 2. Action-Specific Token
        $actionToken = $params['action_token'] ?? null;
        if (!$this->validateActionToken($actionToken)) {
            throw new InvalidActionToken();
        }

        // 3. Rate Limiting
        if ($this->rateLimiter->tooManyAttempts($this->userId)) {
            throw new TooManyAttemptsException();
        }

        // 4. Execute Critical Action
        $this->accountService->delete($this->userId);

        return ComponentUpdate::withMessage('Account deleted');
    }
}

Testing Security

// Security Test Cases
describe('LiveComponent Security', function () {
    it('rejects requests without X-Requested-With header', function () {
        $response = $this->post('/live-component/datatable:demo', [
            'action' => 'sort'
        ]);

        expect($response->status)->toBe(403);
    });

    it('validates component state integrity', function () {
        $tamperedState = ['malicious' => 'data'];

        $response = $this->post('/live-component/datatable:demo', [
            'action' => 'sort',
            'state' => $tamperedState
        ], [
            'X-Requested-With' => 'XMLHttpRequest'
        ]);

        expect($response->status)->toBe(400);
        expect($response->json()['error'])->toContain('State tampering');
    });
});

Zusammenfassung

CSRF und Honeypot sind für LiveComponents deaktiviert, weil:

  1. Technisch inkompatibel mit JSON-basiertem State Management
  2. Architektonisch unnötig durch Component-scoped Security Model
  3. Durch alternative Maßnahmen ersetzt: Origin Validation, State Integrity, Version Control
  4. Best Practice in modernen JavaScript-Frameworks (React, Vue, Angular)

Die Sicherheit wird gewährleistet durch:

  • Origin Validation (Same-Origin-Policy)
  • X-Requested-With Header Validation
  • Component State Integrity (HMAC)
  • Version-based Concurrency Control
  • Session Validation für authentifizierte Actions
  • Optional: Action-Level Tokens für kritische Operations

Dies entspricht dem Security-Model moderner Single-Page Applications und ist die empfohlene Vorgehensweise für AJAX-basierte Component-Systeme.