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.
{
"github_reviewed": true,
"github_reviewed_at": "2026-05-14T20:26:49Z",
"cwe_ids": [
"CWE-862"
],
"severity": "HIGH",
"nvd_published_at": null
}