[GH-ISSUE #20443] Security: Missing Tool Access Control in /api/chat/completions - API bypasses UI restrictions #19187

Closed
opened 2026-04-20 01:31:33 -05:00 by GiteaMirror · 3 comments
Owner

Originally created by @chayaziv on GitHub (Jan 7, 2026).
Original GitHub issue: https://github.com/open-webui/open-webui/issues/20443

Check Existing Issues

  • I have searched for any existing and/or related issues.
  • I have searched for any existing and/or related discussions.
  • I have also searched in the CLOSED issues AND CLOSED discussions and found no related items (your issue might already be addressed on the development branch!).
  • I am using the latest version of Open WebUI.

Installation Method

Docker

Open WebUI Version

v0.6.43

Ollama Version (if applicable)

No response

Operating System

Ubuntu

Browser (if applicable)

No response

Confirmation

  • I have read and followed all instructions in README.md.
  • I am using the latest version of both Open WebUI and Ollama.
  • I have included the browser console logs.
  • I have included the Docker container logs.
  • I have provided every relevant configuration, setting, and environment variable used in my setup.
  • I have clearly listed every relevant configuration, custom setting, environment variable, and command-line option that influences my setup (such as Docker Compose overrides, .env values, browser settings, authentication configurations, etc).
  • I have documented step-by-step reproduction instructions that are precise, sequential, and leave nothing to interpretation. My steps:
  • Start with the initial platform/version/OS and dependencies used,
  • Specify exact install/launch/configure commands,
  • List URLs visited, user input (incl. example values/emails/passwords if needed),
  • Describe all options and toggles enabled or changed,
  • Include any files or environmental changes,
  • Identify the expected and actual result at each stage,
  • Ensure any reasonably skilled user can follow and hit the same issue.

Expected Behavior

When a user without permission to a Tool (MCP/OpenAPI server) tries to use it, access should be denied both in the UI and when calling the API directly via code.

The server should validate tool access on every request, not rely on UI-side filtering.

Actual Behavior

The tool access control check happens ONLY on the client side (UI).

When users access the UI, the frontend calls GET /api/v1/tools which returns only tools they have access to - so they cannot select restricted tools.

However, when users call the API directly via code (Python, curl, etc.), they can use ANY tool by specifying its ID in tool_ids - the server does not validate access.

This is a server-side security vulnerability - access control must be enforced on the server, not just the client.

Steps to Reproduce

  1. Create an MCP Tool Server with access restricted to specific group (e.g., "Admins")
  2. Login as a user who is NOT in that group
  3. Via UI: The tool does NOT appear in the tools list (client-side filtering works)
  4. Via code (bypasses UI): Call API directly:
import requests

# Direct API call - bypasses UI completely
response = [requests.post](http://requests.post/)(
    "http://localhost:8080/api/chat/completions",
    headers={"Authorization": "Bearer <USER_TOKEN>"},
    json={
        "model": "gpt-4",
        "messages": [{"role": "user", "content": "test"}],
        "tool_ids": ["server:mcp:restricted_tool"]  # Tool user has NO access to
    }
)
  1. Result: Server accepts the request and executes the tool

The server trusts that the client already filtered the tools - but code bypasses the client entirely.

Logs & Screenshots

--

Additional Information

🛠 Root Cause Analysis

In backend/open_webui/main.py, the chat_completion function checks model access on the server side, but does NOT check tool access.

Current code in backend/open_webui/main.py lines 1552- 1558:

  # Check if user has access to the model
            if not BYPASS_MODEL_ACCESS_CONTROL and (
                user.role != "admin" or not BYPASS_ADMIN_ACCESS_CONTROL
            ):
                try:
                    check_model_access(user, model)
                except Exception as e:
                    raise e

# ❌ NO server-side tool access check
# tool_ids from request are used directly without validation

Comparison: Where Access Control Works Correctly

UI filtering (client-side only) in backend/open_webui/routers/tools.py lines 143-149:

user_group_ids = {group.id for group in Groups.get_groups_by_member_id(user.id)}
tools = [
    tool for tool in tools
    if tool.user_id ==user.id
    or has_access(user.id, "read", tool.access_control, user_group_ids)
]

This only filters what the UI displays - it does NOT prevent direct API access via code.


💡 Proposed Fix

Add server-side tool access validation in backend/open_webui/main.py after model access check:

from open_webui.routers.tools import get_tools as get_user_tools
     # Check if user has access to the model
            if not BYPASS_MODEL_ACCESS_CONTROL and (
                user.role != "admin" or not BYPASS_ADMIN_ACCESS_CONTROL
            ):
                try:
                    check_model_access(user, model)
                except Exception as e:
                    raise e
        else:
            model = model_item

            request.state.direct = True
            request.state.model = model

       # 💡 Proposed Fix
       # ✅  Add server-side tool access check: 
        if user.role != "admin" or not BYPASS_ADMIN_ACCESS_CONTROL:
            tool_ids = form_data.get("tool_ids", None)
            if tool_ids:
                user_tools = {tool.id for tool in await get_user_tools(request, user)}
                for tool_id in tool_ids:
                    if tool_id not in user_tools:
                        raise Exception(f"You do not have permission to access tool '{tool_id}'")

        model_info_params = (
            model_info.params.model_dump() if model_info and model_info.params else {}
        )

This reuses the same get_tools() function that filters tools for the UI, ensuring consistent server-side enforcement.

Additional Information

Security Impact: Users can bypass UI restrictions and access any tool by calling the API directly via code (Python, curl, Postman, etc.).

Recommended pattern: Access control should ALWAYS be enforced server-side. Client-side filtering is for UX only - never for security.

Originally created by @chayaziv on GitHub (Jan 7, 2026). Original GitHub issue: https://github.com/open-webui/open-webui/issues/20443 ### Check Existing Issues - [x] I have searched for any existing and/or related issues. - [x] I have searched for any existing and/or related discussions. - [x] I have also searched in the CLOSED issues AND CLOSED discussions and found no related items (your issue might already be addressed on the development branch!). - [x] I am using the latest version of Open WebUI. ### Installation Method Docker ### Open WebUI Version v0.6.43 ### Ollama Version (if applicable) _No response_ ### Operating System Ubuntu ### Browser (if applicable) _No response_ ### Confirmation - [x] I have read and followed all instructions in `README.md`. - [x] I am using the latest version of **both** Open WebUI and Ollama. - [x] I have included the browser console logs. - [x] I have included the Docker container logs. - [x] I have **provided every relevant configuration, setting, and environment variable used in my setup.** - [x] I have clearly **listed every relevant configuration, custom setting, environment variable, and command-line option that influences my setup** (such as Docker Compose overrides, .env values, browser settings, authentication configurations, etc). - [x] I have documented **step-by-step reproduction instructions that are precise, sequential, and leave nothing to interpretation**. My steps: - Start with the initial platform/version/OS and dependencies used, - Specify exact install/launch/configure commands, - List URLs visited, user input (incl. example values/emails/passwords if needed), - Describe all options and toggles enabled or changed, - Include any files or environmental changes, - Identify the expected and actual result at each stage, - Ensure any reasonably skilled user can follow and hit the same issue. ### Expected Behavior When a user without permission to a Tool (MCP/OpenAPI server) tries to use it, access should be denied **both in the UI and when calling the API directly via code**. The server should validate tool access on every request, not rely on UI-side filtering. ### Actual Behavior **The tool access control check happens ONLY on the client side (UI).** When users access the UI, the frontend calls `GET /api/v1/tools` which returns only tools they have access to - so they cannot select restricted tools. **However**, when users call the API directly via code (Python, curl, etc.), they can use ANY tool by specifying its ID in `tool_ids` - **the server does not validate access**. This is a **server-side security vulnerability** - access control must be enforced on the server, not just the client. ### Steps to Reproduce 1. Create an MCP Tool Server with access restricted to specific group (e.g., "Admins") 2. Login as a user who is NOT in that group 3. **Via UI:** The tool does NOT appear in the tools list ✅ (client-side filtering works) 4. **Via code (bypasses UI):** Call API directly: ```python import requests # Direct API call - bypasses UI completely response = [requests.post](http://requests.post/)( "http://localhost:8080/api/chat/completions", headers={"Authorization": "Bearer <USER_TOKEN>"}, json={ "model": "gpt-4", "messages": [{"role": "user", "content": "test"}], "tool_ids": ["server:mcp:restricted_tool"] # Tool user has NO access to } ) ``` 5. **Result:** Server accepts the request and executes the tool ❌ **The server trusts that the client already filtered the tools - but code bypasses the client entirely.** ### Logs & Screenshots -- ### Additional Information ## 🛠 Root Cause Analysis In `backend/open_webui/main.py`, the `chat_completion` function checks model access on the server side, but **does NOT check tool access**. **Current code in `backend/open_webui/main.py` lines 1552- 1558:** ```python # Check if user has access to the model if not BYPASS_MODEL_ACCESS_CONTROL and ( user.role != "admin" or not BYPASS_ADMIN_ACCESS_CONTROL ): try: check_model_access(user, model) except Exception as e: raise e # ❌ NO server-side tool access check # tool_ids from request are used directly without validation ``` --- ## ✅ Comparison: Where Access Control Works Correctly **UI filtering (client-side only)** in `backend/open_webui/routers/tools.py` lines 143-149: ```python user_group_ids = {group.id for group in Groups.get_groups_by_member_id(user.id)} tools = [ tool for tool in tools if tool.user_id ==user.id or has_access(user.id, "read", tool.access_control, user_group_ids) ] ``` This only filters what the UI displays - it does NOT prevent direct API access via code. --- ## 💡 Proposed Fix Add **server-side** tool access validation in `backend/open_webui/main.py` after model access check: ```python from open_webui.routers.tools import get_tools as get_user_tools # Check if user has access to the model if not BYPASS_MODEL_ACCESS_CONTROL and ( user.role != "admin" or not BYPASS_ADMIN_ACCESS_CONTROL ): try: check_model_access(user, model) except Exception as e: raise e else: model = model_item request.state.direct = True request.state.model = model # 💡 Proposed Fix # ✅ Add server-side tool access check: if user.role != "admin" or not BYPASS_ADMIN_ACCESS_CONTROL: tool_ids = form_data.get("tool_ids", None) if tool_ids: user_tools = {tool.id for tool in await get_user_tools(request, user)} for tool_id in tool_ids: if tool_id not in user_tools: raise Exception(f"You do not have permission to access tool '{tool_id}'") model_info_params = ( model_info.params.model_dump() if model_info and model_info.params else {} ) ``` This reuses the same `get_tools()` function that filters tools for the UI, ensuring **consistent server-side enforcement**. ## Additional Information **Security Impact:** Users can bypass UI restrictions and access any tool by calling the API directly via code (Python, curl, Postman, etc.). **Recommended pattern:** Access control should ALWAYS be enforced server-side. Client-side filtering is for UX only - never for security.
GiteaMirror added the bug label 2026-04-20 01:31:33 -05:00
Author
Owner

@owui-terminator[bot] commented on GitHub (Jan 7, 2026):

🔍 Similar Issues Found

I found some existing issues that might be related to this one. Please check if any of these are duplicates or contain helpful solutions:

  1. #20414 issue: Chats can't be archived
    by Ithanil • Jan 06, 2026 • bug

  2. #15124 issue: API returns "405 Method Not Allowed" on /v1/chat/completions
    by chisel900 • Jun 18, 2025 • bug

  3. #19987 issue: There is a lack of visual consistency between the home page and the chat interface.
    by i-iooi-i • Dec 16, 2025 • bug


💡 Tips:

  • If this is a duplicate, please consider closing this issue and adding any additional details to the existing one
  • If you found a solution in any of these issues, please share it here to help others

This comment was generated automatically by a bot. Please react with a 👍 if this comment was helpful, or a 👎 if it was not.

<!-- gh-comment-id:3718421077 --> @owui-terminator[bot] commented on GitHub (Jan 7, 2026): 🔍 **Similar Issues Found** I found some existing issues that might be related to this one. Please check if any of these are duplicates or contain helpful solutions: 1. [#20414](https://github.com/open-webui/open-webui/issues/20414) **issue: Chats can't be archived** *by Ithanil • Jan 06, 2026 • `bug`* 2. [#15124](https://github.com/open-webui/open-webui/issues/15124) **issue: API returns "405 Method Not Allowed" on /v1/chat/completions** *by chisel900 • Jun 18, 2025 • `bug`* 3. [#19987](https://github.com/open-webui/open-webui/issues/19987) **issue: There is a lack of visual consistency between the home page and the chat interface.** *by i-iooi-i • Dec 16, 2025 • `bug`* --- 💡 **Tips:** - If this is a duplicate, please consider closing this issue and adding any additional details to the existing one - If you found a solution in any of these issues, please share it here to help others *This comment was generated automatically by a bot.* Please react with a 👍 if this comment was helpful, or a 👎 if it was not.
Author
Owner

@chayaziv commented on GitHub (Jan 7, 2026):

👎
The issues you brought up are not relevant to my issue.

<!-- gh-comment-id:3718443049 --> @chayaziv commented on GitHub (Jan 7, 2026): 👎 The issues you brought up are not relevant to my issue.
Author
Owner

@tjbck commented on GitHub (Jan 7, 2026):

Addressed in dev.

<!-- gh-comment-id:3721278594 --> @tjbck commented on GitHub (Jan 7, 2026): Addressed in dev.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/open-webui#19187