[PR #21631] [MERGED] fix: race condition in signup allows multiple admin accounts #49219

Closed
opened 2026-04-30 01:32:46 -05:00 by GiteaMirror · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/open-webui/open-webui/pull/21631
Author: @theeggorchicken
Created: 2/20/2026
Status: Merged
Merged: 2/21/2026
Merged by: @tjbck

Base: devHead: fix/signup-race-condition


📝 Commits (1)

  • c86a69f fix: race condition in signup allows multiple admin accounts

📊 Changes

1 file changed (+11 additions, -7 deletions)

View changed files

📝 backend/open_webui/routers/auths.py (+11 -7)

📄 Description

Pull Request Checklist

  • Target branch: Verify that the pull request targets the dev branch.
  • Description: Provided below.
  • Changelog: Included below.
  • Documentation: N/A — internal code fix, no user-facing API changes.
  • Dependencies: No new dependencies.
  • Testing: Tested with 4 uvicorn workers and 30 concurrent signup requests. Before fix: 4 admin accounts created. After fix: only 1 admin.
  • Agentic AI Code: This fix has been human-reviewed and manually tested against a running Open WebUI instance.
  • Code review: Self-reviewed.
  • Design & Architecture: Minimal change — no new settings, no UI changes.
  • Git Hygiene: Single atomic commit.
  • Title Prefix: fix:

Changelog Entry

Description

Race condition in signup_handler allows multiple admin account registrations on fresh deployments when running with multiple uvicorn workers.

Fixed

  • TOCTOU race in signup_handler where has_users() check is not atomic with insert_new_auth(). With multiple workers, concurrent signup requests during first-user registration can all see an empty user table and each receive the admin role.

Security

  • Prevents unauthorized admin privilege escalation during initial setup on multi-worker deployments.

Vulnerable Lines

File: backend/open_webui/routers/auths.py#L703-L709

has_users = Users.has_users(db=db)                    # CHECK
role = "admin" if not has_users else ...               # DECIDE
hashed = get_password_hash(password)                   # bcrypt ~100ms window
user = Auths.insert_new_auth(..., role=role, db=db)   # USE (stale check)

What could happen

On a fresh Open WebUI deployment with multiple uvicorn workers (--workers >= 2), an attacker can send concurrent signup requests during first-user registration. Each worker's has_users() returns False before any insert completes, so multiple accounts get the admin role. I tested with 4 workers and 30 concurrent requests — 4 accounts got admin, each with full admin privileges (listing users, reading config, managing the instance).

How to verify

  1. Start Open WebUI with UVICORN_WORKERS=4 and ENABLE_SIGNUP=true
  2. On a fresh instance (no existing users), fire 20+ concurrent POST requests to /api/v1/auths/signup with different email addresses
  3. Check how many responses contain "role": "admin" — without this fix, multiple will

What this PR does

Reverses the signup flow: insert the user with the default role first, then atomically check if the user count is 1 (meaning this is the only user). Only then promote to admin. This closes the race window because after the insert, the count includes the just-inserted row — if two workers insert concurrently, both will see count >= 2 and neither gets promoted.

Evidence

Full race condition exploit traces: https://gist.github.com/theeggorchicken/455f6c46ea73bd96396400a679c3c207

Contributor License Agreement

By submitting this pull request, I confirm that I have read and fully agree to the Contributor License Agreement (CLA), and I am providing my contributions under its terms.


🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/open-webui/open-webui/pull/21631 **Author:** [@theeggorchicken](https://github.com/theeggorchicken) **Created:** 2/20/2026 **Status:** ✅ Merged **Merged:** 2/21/2026 **Merged by:** [@tjbck](https://github.com/tjbck) **Base:** `dev` ← **Head:** `fix/signup-race-condition` --- ### 📝 Commits (1) - [`c86a69f`](https://github.com/open-webui/open-webui/commit/c86a69f6197d7197cf1709a5346673f863aebaa8) fix: race condition in signup allows multiple admin accounts ### 📊 Changes **1 file changed** (+11 additions, -7 deletions) <details> <summary>View changed files</summary> 📝 `backend/open_webui/routers/auths.py` (+11 -7) </details> ### 📄 Description <!-- ⚠️ CRITICAL CHECKS FOR CONTRIBUTORS (READ, DON'T DELETE) ⚠️ 1. Target the `dev` branch. PRs targeting `main` will be automatically closed. 2. Do NOT delete the CLA section at the bottom. It is required for the bot to accept your PR. --> # Pull Request Checklist - [x] **Target branch:** Verify that the pull request targets the `dev` branch. - [x] **Description:** Provided below. - [x] **Changelog:** Included below. - [ ] **Documentation:** N/A — internal code fix, no user-facing API changes. - [ ] **Dependencies:** No new dependencies. - [x] **Testing:** Tested with 4 uvicorn workers and 30 concurrent signup requests. Before fix: 4 admin accounts created. After fix: only 1 admin. - [x] **Agentic AI Code:** This fix has been human-reviewed and manually tested against a running Open WebUI instance. - [x] **Code review:** Self-reviewed. - [x] **Design & Architecture:** Minimal change — no new settings, no UI changes. - [x] **Git Hygiene:** Single atomic commit. - [x] **Title Prefix:** `fix:` # Changelog Entry ### Description Race condition in `signup_handler` allows multiple admin account registrations on fresh deployments when running with multiple uvicorn workers. ### Fixed - TOCTOU race in `signup_handler` where `has_users()` check is not atomic with `insert_new_auth()`. With multiple workers, concurrent signup requests during first-user registration can all see an empty user table and each receive the admin role. ### Security - Prevents unauthorized admin privilege escalation during initial setup on multi-worker deployments. --- ### Vulnerable Lines **File:** [`backend/open_webui/routers/auths.py#L703-L709`](https://github.com/open-webui/open-webui/blob/main/backend/open_webui/routers/auths.py#L703-L709) ```python has_users = Users.has_users(db=db) # CHECK role = "admin" if not has_users else ... # DECIDE hashed = get_password_hash(password) # bcrypt ~100ms window user = Auths.insert_new_auth(..., role=role, db=db) # USE (stale check) ``` ### What could happen On a fresh Open WebUI deployment with multiple uvicorn workers (`--workers >= 2`), an attacker can send concurrent signup requests during first-user registration. Each worker's `has_users()` returns False before any insert completes, so multiple accounts get the admin role. I tested with 4 workers and 30 concurrent requests — 4 accounts got admin, each with full admin privileges (listing users, reading config, managing the instance). ### How to verify 1. Start Open WebUI with `UVICORN_WORKERS=4` and `ENABLE_SIGNUP=true` 2. On a fresh instance (no existing users), fire 20+ concurrent POST requests to `/api/v1/auths/signup` with different email addresses 3. Check how many responses contain `"role": "admin"` — without this fix, multiple will ### What this PR does Reverses the signup flow: insert the user with the default role first, then atomically check if the user count is 1 (meaning this is the only user). Only then promote to admin. This closes the race window because after the insert, the count includes the just-inserted row — if two workers insert concurrently, both will see count >= 2 and neither gets promoted. ### Evidence Full race condition exploit traces: https://gist.github.com/theeggorchicken/455f6c46ea73bd96396400a679c3c207 ### Contributor License Agreement By submitting this pull request, I confirm that I have read and fully agree to the [Contributor License Agreement (CLA)](https://github.com/open-webui/open-webui/blob/main/CONTRIBUTOR_LICENSE_AGREEMENT), and I am providing my contributions under its terms. --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
GiteaMirror added the pull-request label 2026-04-30 01:32:46 -05:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/open-webui#49219