MEDIUM: Setup endpoint race condition — multiple admin users can be created #26

Closed
opened 2026-02-16 05:47:14 +01:00 by clawbot · 1 comment
Collaborator

Bug

Files:

  • internal/handlers/setup.go, HandleSetupPOST()
  • internal/service/auth/auth.go, CreateUser()
  • internal/middleware/middleware.go, SetupRequired()

Severity: MEDIUM — Authentication bypass

Description

There is a TOCTOU (Time-of-Check-Time-of-Use) race condition in the setup flow:

  1. SetupRequired() middleware checks if any users exist
  2. HandleSetupPOST() processes the form
  3. CreateUser() checks UserExists() again, then inserts

If two requests arrive simultaneously (e.g., automated attack while the setup page is accessible), both can pass the UserExists() check before either has inserted a user. The second insert may succeed (SQLite doesn't have a UNIQUE constraint on username based on the migration), creating a second admin account.

Even if the second CreateUser returns ErrUserExists, the middleware check at step 1 happens independently for each request.

Suggested Fix

  1. Add a UNIQUE constraint on users.username in the database schema
  2. Use a mutex in CreateUser or use an INSERT with conflict handling:
    INSERT INTO users (username, password_hash) VALUES (?, ?)
    ON CONFLICT(username) DO NOTHING
    
  3. The CreateUser function should check row count after insert to detect conflicts
## Bug **Files:** - `internal/handlers/setup.go`, `HandleSetupPOST()` - `internal/service/auth/auth.go`, `CreateUser()` - `internal/middleware/middleware.go`, `SetupRequired()` **Severity:** MEDIUM — Authentication bypass ### Description There is a TOCTOU (Time-of-Check-Time-of-Use) race condition in the setup flow: 1. `SetupRequired()` middleware checks if any users exist 2. `HandleSetupPOST()` processes the form 3. `CreateUser()` checks `UserExists()` again, then inserts If two requests arrive simultaneously (e.g., automated attack while the setup page is accessible), both can pass the `UserExists()` check before either has inserted a user. The second insert may succeed (SQLite doesn't have a UNIQUE constraint on username based on the migration), creating a second admin account. Even if the second `CreateUser` returns `ErrUserExists`, the middleware check at step 1 happens independently for each request. ### Suggested Fix 1. Add a UNIQUE constraint on `users.username` in the database schema 2. Use a mutex in `CreateUser` or use an INSERT with conflict handling: ```sql INSERT INTO users (username, password_hash) VALUES (?, ?) ON CONFLICT(username) DO NOTHING ``` 3. The `CreateUser` function should check row count after insert to detect conflicts
Owner

implement 1 and 2 and give me a PR

implement 1 and 2 and give me a PR
sneak closed this issue 2026-02-16 06:45:02 +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#26
No description provided.