Any authenticated user with low privileges can enumerate active background tasks across the system and stop tasks belonging to other users via the GET /api/tasks and POST /api/tasks/stop/{task_id} methods. This allows a casual user to disrupt system-wide chat usage by continuously canceling other users' active tasks. This is a real authorization vulnerability affecting integrity and usability in multi-user deployments.
Open WebUI exposes GET /api/tasks and POST /api/tasks/stop/{task_id} to any verified user. These endpoints operate on a global task namespace and accept raw task_id values without checking whether the task belongs to the current caller.
As a result, a normal authenticated user can enumerate active global task IDs and stop tasks belonging to other users.
Root cause:
In backend/open_webui/main.py, both endpoints only require get_verified_user:
@app.post('/api/tasks/stop/{task_id}')
async def stop_task_endpoint(request: Request, task_id: str, user=Depends(get_verified_user)):
result = await stop_task(request.app.state.redis, task_id)
@app.get('/api/tasks')
async def list_tasks_endpoint(request: Request, user=Depends(get_verified_user)):
return {'tasks': await list_tasks(request.app.state.redis)}
get_verified_user accepts both user and admin roles in backend/open_webui/utils/auth.py.
In backend/open_webui/tasks.py, task listing is global:
async def list_tasks(redis):
if redis:
return await redis_list_tasks(redis)
return list(tasks.keys())
In backend/open_webui/tasks.py, task stopping is by raw task_id:
async def stop_task(redis, task_id: str):
if redis:
item_id = await redis.hget(REDIS_TASKS_KEY, task_id)
await redis_send_command(redis, {'action': 'stop', 'task_id': task_id})
await redis_cleanup_task(redis, task_id, item_id or None)
There is no owner check, no user_id check, and no mapping from task_id back to the current caller before stop or cleanup.
This also appears unintended because the codebase already has a scoped route, GET /api/tasks/chat/{chat_id}, which checks whether the chat belongs to the current user before returning task IDs.
Relevant code references:
- backend/open_webui/main.py:1975
- backend/open_webui/main.py:1984
- backend/open_webui/main.py:1989
- backend/open_webui/tasks.py:127
- backend/open_webui/tasks.py:145
- backend/open_webui/utils/auth.py:415
Suggested remediation:
- Store task ownership metadata such as user_id and chat_id, then enforce owner-only access for non-admin users
- Suggested implementation locations:
- backend/open_webui/main.py: add authentication checks for /api/tasks and /api/tasks/stop/{task_id}
- backend/open_webui/tasks.py: add support for storing/querying task ownership metadata such as user_id and chat_id, and support owner-scoped listing/stopping
Preconditions:
main branch deploymentThis does not require any weakened security settings.
PoC objective:
Reproduction steps:
Using the UI, User A starts a long response generation or another background task and leaves it running.
Expected security model:
User B should not be able to see or control User A's task.
Using User B's authenticated token:
curl -i -H "Authorization: Bearer <USER_B_TOKEN>" http://<open-webui-host>/api/tasks
Expected result:
Actual result:
the response returns the global active task list.
Example response shape:
{"tasks":["<task-id-a>","<task-id-b>"]}
This exposes task IDs belonging to other users.
Pick a task ID that belongs to User A and send:
curl -i -X POST -H "Authorization: Bearer <USER_B_TOKEN>" http://<open-webui-host>/api/tasks/stop/<FOREIGN_TASK_ID>
Expected result:
403 Forbidden, or404 Not Found for non-owned tasks, orActual result:
the server accepts the request and attempts to stop the foreign task.
Example response shape:
{"status":true,"message":"Task <FOREIGN_TASK_ID> stopped."}
User A's running task is interrupted or disappears from the active task set even though User B does not own it.
What actions become possible that should not be possible:
Copy-paste PoC summary:
curl -s -H "Authorization: Bearer <USER_B_TOKEN>" http://<open-webui-host>/api/tasks
curl -s -X POST -H "Authorization: Bearer <USER_B_TOKEN>" http://<open-webui-host>/api/tasks/stop/<FOREIGN_TASK_ID>
Type of vulnerability:
broken object-level authorization affecting a global runtime control-plane endpoint.
Who is impacted:
Direct impact:
Practical impact:
/api/tasks and immediately cancel every newly created active taskWhy severity is meaningful:
This is not full account takeover or privilege escalation, but it enables platform-wide operational disruption from a low-privilege account. In practice, sustained exploitation can make chat functionality effectively unusable for other users on the system.
Fixed in commit e7ff4768f (#23454, "Add ownership checks to global task endpoints"), first released in v0.9.0 (Apr 2026).
The fix takes a simpler approach than per-task ownership tracking, which would have required a schema change to attribute every task to a user_id:
GET /api/tasks and POST /api/tasks/stop/{task_id} are restricted to admin-only via Depends(get_admin_user). Cross-user enumeration and termination are no longer reachable from a non-admin account.POST /api/tasks/chat/{chat_id}/stop endpoint covers the legitimate non-admin use case (a user stopping their own in-progress generation), reusing the same chat-ownership check the existing GET /api/tasks/chat/{chat_id} already enforces.CVE-2025-63681 was a prior disclosure of the same authorization gap against v0.6.33; the fix in v0.9.0 also resolves that.
Users on >= 0.9.0 are not affected.
| Score | Percentile |
|---|---|
| 0.03% | 10.41% |
| Base score | Version | Severity | Vector |
|---|---|---|---|
| 7.1 | 3.1 | — |
|
| Type | Value |
|---|---|
| GHSA | GHSA-8jjp-r2w2-4v22 ↗ |
| CVE | CVE-2026-45399 ↗ |
| CWE id | Name |
|---|---|
| CWE-862 | Missing Authorization |
Vulnerable version ranges and first patched releases as published by GitHub.
| Ecosystem | Package | Vulnerable range | First patched | Vulnerable functions |
|---|---|---|---|---|
| pip | open-webui | <= 0.8.12 | 0.9.0 | — |