CRITICAL: API v1 routes use cookie auth without CSRF protection — cross-site request forgery #112

Closed
opened 2026-02-20 13:50:46 +01:00 by clawbot · 1 comment
Collaborator

Summary

The API v1 routes (/api/v1/*) use cookie-based session authentication but explicitly bypass CSRF protection. This means any malicious website can make authenticated requests on behalf of a logged-in user.

Location

internal/server/routes.go — lines in SetupRoutes():

// API v1 routes (cookie-based session auth, no CSRF)
s.router.Route("/api/v1", func(r chi.Router) {
    r.With(s.mw.LoginRateLimit()).Post("/login", s.handlers.HandleAPILoginPOST())
    r.Group(func(r chi.Router) {
        r.Use(s.mw.APISessionAuth())
        r.Delete("/apps/{id}", s.handlers.HandleAPIDeleteApp())
        r.Post("/apps/{id}/deploy", s.handlers.HandleAPITriggerDeploy())
        // ... etc
    })
})

The comment even acknowledges it: cookie-based session auth, no CSRF.

Impact

An attacker can create a webpage that, when visited by a logged-in upaas admin:

  1. Deletes all apps via DELETE /api/v1/apps/{id}
  2. Triggers deployments via POST /api/v1/apps/{id}/deploy
  3. Creates malicious apps via POST /api/v1/apps
  4. Reads app secrets (webhook secrets, SSH keys) via GET /api/v1/apps/{id} (if CORS allows, or via form submission)

This requires only that the victim visits a malicious page while logged in.

Suggested Fix

Either:

  1. Add CSRF tokens to API routes — require X-CSRF-Token header (from a cookie or meta tag)
  2. Switch API to token-based auth — use Authorization: Bearer <token> instead of cookies. This is immune to CSRF since browsers don't auto-attach custom headers.
  3. Require a custom header — e.g., X-Requested-With: XMLHttpRequest. Browsers won't send custom headers in cross-origin simple requests.

Option 2 or 3 is recommended for APIs.

Severity

CRITICAL — Any logged-in admin visiting a malicious page can have their entire PaaS infrastructure destroyed or compromised.

## Summary The API v1 routes (`/api/v1/*`) use **cookie-based session authentication** but explicitly bypass CSRF protection. This means any malicious website can make authenticated requests on behalf of a logged-in user. ## Location `internal/server/routes.go` — lines in `SetupRoutes()`: ```go // API v1 routes (cookie-based session auth, no CSRF) s.router.Route("/api/v1", func(r chi.Router) { r.With(s.mw.LoginRateLimit()).Post("/login", s.handlers.HandleAPILoginPOST()) r.Group(func(r chi.Router) { r.Use(s.mw.APISessionAuth()) r.Delete("/apps/{id}", s.handlers.HandleAPIDeleteApp()) r.Post("/apps/{id}/deploy", s.handlers.HandleAPITriggerDeploy()) // ... etc }) }) ``` The comment even acknowledges it: `cookie-based session auth, no CSRF`. ## Impact An attacker can create a webpage that, when visited by a logged-in upaas admin: 1. **Deletes all apps** via `DELETE /api/v1/apps/{id}` 2. **Triggers deployments** via `POST /api/v1/apps/{id}/deploy` 3. **Creates malicious apps** via `POST /api/v1/apps` 4. **Reads app secrets** (webhook secrets, SSH keys) via `GET /api/v1/apps/{id}` (if CORS allows, or via form submission) This requires only that the victim visits a malicious page while logged in. ## Suggested Fix Either: 1. **Add CSRF tokens to API routes** — require `X-CSRF-Token` header (from a cookie or meta tag) 2. **Switch API to token-based auth** — use `Authorization: Bearer <token>` instead of cookies. This is immune to CSRF since browsers don't auto-attach custom headers. 3. **Require a custom header** — e.g., `X-Requested-With: XMLHttpRequest`. Browsers won't send custom headers in cross-origin simple requests. Option 2 or 3 is recommended for APIs. ## Severity **CRITICAL** — Any logged-in admin visiting a malicious page can have their entire PaaS infrastructure destroyed or compromised.
Owner

disable the api’s write methods.

disable the api’s write methods.
clawbot was assigned by sneak 2026-02-20 14:31:08 +01:00
sneak closed this issue 2026-02-20 14:35:13 +01:00
Sign in to join this conversation.
No Milestone
No project
No Assignees
2 Participants
Notifications
Due Date
The due date is invalid or out of range. Please use the format 'yyyy-mm-dd'.

No due date set.

Dependencies

No dependencies set.

Reference: sneak/upaas#112
No description provided.