Uptime Kuma CSRF Protection and Origin Security: How It Works
Uptime Kuma eliminates traditional CSRF vulnerabilities by using JWT-based authentication instead of cookie-based sessions, while enforcing strict WebSocket origin validation and iframe protection through configurable security headers.
Uptime Kuma, an open-source monitoring tool maintained by Louis Lam, implements a unique security model that abandons cookie-based authentication entirely. By leveraging JSON Web Tokens (JWT) transmitted explicitly in headers and socket.io events, the application removes the attack surface that traditional CSRF protections target. This article examines the specific origin-checking mechanisms and security headers implemented in the louislam/uptime-kuma repository.
How Uptime Kuma Eliminates CSRF Without Tokens
Unlike conventional web applications that rely on session cookies and require synchronizer tokens to prevent cross-site request forgery, Uptime Kuma uses a token-based authentication system that inherently mitigates CSRF attacks.
In server/util-server.js, the authentication utilities including checkLogin validate JWTs passed explicitly in request bodies or socket.io events rather than extracting credentials from cookies. Because browsers do not automatically attach custom headers or request body parameters to cross-origin requests, an attacker cannot forge authenticated requests even without a CSRF token. This design choice eliminates the need for the csurf middleware or similar server-side CSRF protection mechanisms.
WebSocket Origin Validation
While JWT-based auth removes cookie-CSRF risks, the WebSocket connections require explicit origin verification to prevent unauthorized cross-origin socket connections.
The allowRequest Hook Implementation
In server/uptime-kuma-server.js, the Socket.io server constructor implements an allowRequest hook that validates every incoming WebSocket connection:
// Conceptual representation of the implementation
allowRequest: (req, callback) => {
const origin = req.headers.origin;
// Validation checks origin against Host or x-forwarded-for
if (originMatchesTrustedHost(origin, req)) {
callback(null, true);
} else {
callback(null, false);
}
}
The hook extracts req.headers.origin and compares it against the request's Host header or the trusted proxy header x-forwarded-for. If the origin does not match these trusted sources, the connection is rejected with callback(null, false) and an error is logged. This ensures that WebSocket connections originate only from the expected domain.
Bypassing the Origin Check for Reverse Proxies
For deployments behind complex reverse proxy configurations, you can disable this check:
export UPTIME_KUMA_WS_ORIGIN_CHECK=bypass
node server/server.js
Setting UPTIME_KUMA_WS_ORIGIN_CHECK to "bypass" instructs the server in server/uptime-kuma-server.js to skip origin validation entirely, allowing connections from any origin.
HTTP Security Headers and Frame Protection
Uptime Kuma implements additional security layers through HTTP headers to prevent click-jacking and control cross-origin resource sharing.
X-Frame-Options Configuration
In server/server.js, the Express server adds the X-Frame-Options: SAMEORIGIN header to every HTTP response by default. This prevents the Uptime Kuma dashboard from being embedded in malicious iframes, thwarting click-jacking attacks.
To disable this protection (rarely needed):
# Via CLI flag
node server/server.js --disable-frame-sameorigin
# Or via environment variable
export UPTIME_KUMA_DISABLE_FRAME_SAMEORIGIN=1
node server/server.js
CORS Policies for Development vs Production
The server/util-server.js file contains helper functions allowDevAllOrigin and allowAllOrigin that configure Cross-Origin Resource Sharing headers based on the environment:
- Development mode (
NODE_ENV=development): SendsAccess-Control-Allow-Origin: *headers, allowing the UI to be served from a different port during local development. - Production mode: No CORS headers are added, forcing the UI and API to be served from the same origin and preventing cross-origin API requests.
JWT Extraction in the Client
The client-side application stores and transmits JWTs explicitly rather than relying on cookies. In src/mixins/socket.js, the Vue mixin extracts the stored token:
const jwtToken = this.$root.storage().token;
if (jwtToken && jwtToken !== 'autoLogin') {
const payload = jwtDecode(jwtToken);
// Token used for socket.io authentication
}
This explicit token handling ensures that authentication credentials are never automatically included in cross-site requests.
Summary
- JWT-based authentication in
server/util-server.jseliminates CSRF attack vectors by avoiding cookie-based sessions entirely. - WebSocket origin validation in
server/uptime-kuma-server.jsrejects connections from untrusted origins unless bypassed viaUPTIME_KUMA_WS_ORIGIN_CHECK. - X-Frame-Options: SAMEORIGIN headers in
server/server.jsprevent click-jacking attacks by default. - Environment-specific CORS controls allow development flexibility while enforcing same-origin policies in production.
- No CSRF tokens are required or implemented, as the authentication architecture inherently prevents cross-site request forgery.
Frequently Asked Questions
Does Uptime Kuma use CSRF tokens for security?
No, Uptime Kuma does not implement traditional CSRF tokens or the csurf middleware. Because the application uses JWT-based authentication stored in browser memory (not cookies), browsers cannot automatically include credentials in cross-site requests. This architectural choice inherently prevents CSRF attacks without requiring synchronizer tokens.
How do I configure Uptime Kuma behind a reverse proxy with WebSocket support?
When running behind a reverse proxy like Nginx or Traefik, set the UPTIME_KUMA_WS_ORIGIN_CHECK environment variable to "bypass" to disable the strict origin check. This allows the WebSocket connections to function correctly when the proxy modifies headers or when the origin appears different due to forwarding. Ensure your proxy correctly sets X-Forwarded-For headers if you choose to keep origin checking enabled.
What is the purpose of the X-Frame-Options header in Uptime Kuma?
The X-Frame-Options: SAMEORIGIN header prevents click-jacking attacks by ensuring the Uptime Kuma dashboard can only be embedded in iframes on the same origin. This stops attackers from loading your monitoring interface within a malicious site using invisible frames to trick users into unintended actions. You can disable this with the --disable-frame-sameorigin flag if you need to embed the dashboard in legitimate cross-origin frames.
Is CORS enabled in production Uptime Kuma deployments?
No, Uptime Kuma does not send CORS headers in production environments (NODE_ENV=production). The allowAllOrigin and allowDevAllOrigin functions in server/util-server.js only add Access-Control-Allow-Origin: * during development mode. In production, the API and UI must be served from the same origin, which provides additional CSRF protection by preventing cross-origin API requests from browser-based JavaScript.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →