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:
- The cookie is set by the backend during login with
HttpOnly,Secure, andSameSiteflags - The axios client has
withCredentials: true, enabling cookie transmission
Backend Changes
Location: backend/internal/api/middleware/auth.go
Authentication priority order:
- Authorization header (Bearer token) - for API clients
- auth_token cookie (HttpOnly) - preferred for browsers and WebSockets
- 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.
Cookie Configuration
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) orLax(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 worksTestAuthMiddleware_QueryParamFallback- verifies deprecated query param still worksTestAuthMiddleware_PrefersCookieOverQueryParam- verifies cookie is prioritized over query paramTestAuthMiddleware_PrefersAuthorizationHeader- verifies header takes highest priority
Log Verification
To verify tokens are not logged:
-
Before the fix: Check Caddy access logs for token exposure:
docker logs charon 2>&1 | grep "token=" | grep -o "token=[^&]*"Would show:
token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... -
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
- Current: Query parameter authentication is deprecated but still functional
- Future (v2.0): Query parameter authentication will be removed entirely
- Recommendation: Any custom scripts or tools should migrate to using Authorization headers or cookie-based authentication