Files
Charon/docs/security/websocket-auth-security.md
2025-12-17 12:56:46 +00:00

4.4 KiB

WebSocket Authentication Security

Overview

This document explains the security improvements made to WebSocket authentication in Charon to prevent JWT tokens from being exposed in access logs.

Security Issue

Before (Insecure)

Previously, WebSocket connections authenticated by passing the JWT token as a query parameter:

wss://example.com/api/v1/logs/live?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

Security Risk:

  • Query parameters are logged in web server access logs (Caddy, nginx, Apache, etc.)
  • Tokens appear in proxy logs
  • Tokens may be stored in browser history
  • Tokens can be captured in monitoring and telemetry systems
  • An attacker with access to these logs can replay the token to impersonate a user

After (Secure)

WebSocket connections now authenticate using HttpOnly cookies:

wss://example.com/api/v1/logs/live?source=waf&level=error

The browser automatically sends the auth_token cookie with the WebSocket upgrade request.

Security Benefits:

  • HttpOnly cookies are not logged by web servers
  • HttpOnly cookies cannot be accessed by JavaScript (XSS protection)
  • Cookies are not visible in browser history
  • Cookies are not captured in URL-based monitoring
  • Token replay attacks are mitigated (tokens still have expiration)

Implementation Details

Frontend Changes

Location: frontend/src/api/logs.ts

Removed:

const token = localStorage.getItem('charon_auth_token');
if (token) {
  params.append('token', token);
}

The browser automatically sends the auth_token cookie when establishing WebSocket connections due to:

  1. The cookie is set by the backend during login with HttpOnly, Secure, and SameSite flags
  2. The axios client has withCredentials: true, enabling cookie transmission

Backend Changes

Location: backend/internal/api/middleware/auth.go

Authentication priority order:

  1. Authorization header (Bearer token) - for API clients
  2. auth_token cookie (HttpOnly) - preferred for browsers and WebSockets
  3. token query parameter - deprecated, kept for backward compatibility only

The query parameter fallback is marked as deprecated and will be removed in a future version.

Location: backend/internal/api/handlers/auth_handler.go

The auth_token cookie is set with security best practices:

  • HttpOnly: true - prevents JavaScript access (XSS protection)
  • Secure: true (in production with HTTPS) - prevents transmission over HTTP
  • SameSite: Strict (HTTPS) or Lax (HTTP/IP) - CSRF protection
  • Path: / - available for all routes
  • MaxAge: 24 hours - automatic expiration

Verification

Test Coverage

Location: backend/internal/api/middleware/auth_test.go

  • TestAuthMiddleware_Cookie - verifies cookie authentication works
  • TestAuthMiddleware_QueryParamFallback - verifies deprecated query param still works
  • TestAuthMiddleware_PrefersCookieOverQueryParam - verifies cookie is prioritized over query param
  • TestAuthMiddleware_PrefersAuthorizationHeader - verifies header takes highest priority

Log Verification

To verify tokens are not logged:

  1. Before the fix: Check Caddy access logs for token exposure:

    docker logs charon 2>&1 | grep "token=" | grep -o "token=[^&]*"
    

    Would show: token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

  2. After the fix: Check that WebSocket URLs are clean:

    docker logs charon 2>&1 | grep "/logs/live\|/cerberus/logs/ws"
    

    Shows: /api/v1/logs/live?source=waf&level=error (no token)

Migration Path

For Users

No action required. The change is transparent:

  • Login sets the HttpOnly cookie
  • WebSocket connections automatically use the cookie
  • Existing sessions continue to work

For API Clients

API clients using Authorization headers are unaffected.

Deprecation Timeline

  1. Current: Query parameter authentication is deprecated but still functional
  2. Future (v2.0): Query parameter authentication will be removed entirely
  3. Recommendation: Any custom scripts or tools should migrate to using Authorization headers or cookie-based authentication