{"id":"GHSA-w2j7-f3c6-g8cw","title":"Flask-Security has an Open Redirect issue","summary":"Flask-Security has an Open Redirect issue","severity":"medium","cvss":4.7,"cwe":["CWE-601"],"vendor":"Flask-Security","product":"Flask-Security","ecosystem":"pip","affected":["Flask-Security <= 5.8.0"],"patched":["Flask-Security 5.8.1"],"published":"2026-06-23","updated":"2026-06-23","source":"GHSA","sourceUrl":"https://github.com/advisories/GHSA-w2j7-f3c6-g8cw","references":[{"url":"https://github.com/pallets-eco/flask-security/security/advisories/GHSA-w2j7-f3c6-g8cw"},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2023-49438"},{"url":"https://advisories.gitlab.com/pypi/flask-security-too/CVE-2023-49438"},{"url":"https://osv.dev/vulnerability/CVE-2023-49438"},{"url":"https://github.com/advisories/GHSA-w2j7-f3c6-g8cw"}],"tags":["ghsa","pip"],"ingestedAt":"2026-06-26T16:43:14.591Z","slug":"GHSA-w2j7-f3c6-g8cw","body":"## Overview\n\n# Open Redirect in Flask-Security\n\n## Summary\n\n`flask_security.utils.validate_redirect_url()` can allow an attacker-controlled redirect URL when subdomain redirects are enabled.\n\nThe bypass uses a backslash inside the URL authority/host:\n\n```text\nhttp://evil.com\\.whitelist.com\nhttp://evil.com%5C.whitelist.com\n```\n\nPython's `urlsplit()` parses the full authority as `evil.com\\.whitelist.com` or `evil.com%5C.whitelist.com`. Because the value ends with `.whitelist.com`, `validate_redirect_url()` accepts it as an allowed subdomain of `whitelist.com`.\n\nThis is similar in class to the previous Flask-Security-Too open redirect advisory CVE-2023-49438 / GHSA-672h-6x89-76m5, where crafted redirect URLs bypassed validation through browser URL normalization behavior.\n\n## Affected Configuration\n\nThe issue requires subdomain redirects to be enabled:\n\n```python\nSERVER_NAME = \"whitelist.com\"\nSECURITY_REDIRECT_ALLOW_SUBDOMAINS = True\n```\n\nTested environment:\n\n```text\nFlask-Security: 5.8.0\nFlask: 3.1.3\nWerkzeug: 3.1.8\n```\n\n## Impact\n\nAn attacker can craft a URL that passes Flask-Security's redirect validation and produces a `302` response to an attacker-controlled URL-like authority.\n\nThis can be used for phishing or other attacks that rely on a trusted application redirecting users to an attacker-controlled destination.\n\n## Proof of Concept\n\n### PoC Flask App\n\n```python\nfrom __future__ import annotations\n\nfrom importlib.metadata import version\nfrom urllib.parse import urlsplit\n\nfrom flask import Flask, jsonify, redirect, request\n\nfrom flask_security.utils import validate_redirect_url\n\n\napp = Flask(__name__)\napp.config.update(\n    SECRET_KEY=\"poc-only\",\n    SERVER_NAME=\"whitelist.com\",\n    SECURITY_REDIRECT_ALLOW_SUBDOMAINS=True,\n    SECURITY_REDIRECT_BASE_DOMAIN=None,\n    SECURITY_REDIRECT_ALLOWED_SUBDOMAINS=[],\n)\n\n\n@app.get(\"/\")\ndef index():\n    return jsonify(\n        flask_version=version(\"Flask\"),\n        configured_server_name=app.config[\"SERVER_NAME\"],\n        examples=[\n            r\"http://evil.com\\.whitelist.com\",\n            \"http://evil.com%5C.whitelist.com\",\n            \"http://sub.whitelist.com\",\n            \"http://sub.not-whitelist.com\",\n        ],\n    )\n\n\n@app.get(\"/check\")\ndef check():\n    next_url = request.args.get(\"next\", \"\")\n    parsed = urlsplit(next_url)\n\n    return jsonify(\n        next=next_url,\n        valid=validate_redirect_url(next_url),\n        parsed={\n            \"scheme\": parsed.scheme,\n            \"netloc\": parsed.netloc,\n            \"hostname\": parsed.hostname,\n            \"path\": parsed.path,\n        },\n    )\n\n\n@app.get(\"/redir\")\ndef redir():\n    next_url = request.args.get(\"next\", \"\")\n    if not validate_redirect_url(next_url):\n        return jsonify(error=\"blocked\", next=next_url), 400\n\n    return redirect(next_url)\n\n\nif __name__ == \"__main__\":\n    app.run(host=\"127.0.0.1\", port=5000, debug=False)\n```\n\n### Steps to Reproduce\n\nRun the PoC with the target project's Flask version:\n\n```bash\n.venv/bin/python poc_redirect_app.py\n```\n\nThe invalid comparison case is correctly blocked:\n\n```bash\nhttp://127.0.0.1:5000/redir?next=http://evil.com\n```\n\nObserved result:\n\n<img width=\"1019\" height=\"294\" alt=\"image\" src=\"https://github.com/user-attachments/assets/de25ac4d-b37f-4369-928e-f44dfd5b7557\" />\n\nCheck the validation result:\n\n```bash\nhttp://127.0.0.1:5000/check?next=http://evil.com%5C.whitelist.com\n```\n\nObserved result:\n\n<img width=\"1029\" height=\"634\" alt=\"image\" src=\"https://github.com/user-attachments/assets/8e5ec8a6-42a4-438a-8d12-a27724519091\" />\n\n## References\n\n- CVE-2023-49438: https://advisories.gitlab.com/pypi/flask-security-too/CVE-2023-49438/\n- GHSA-672h-6x89-76m5: https://osv.dev/vulnerability/CVE-2023-49438\n- NVD entry for CVE-2023-49438: https://nvd.nist.gov/vuln/detail/CVE-2023-49438\n\n## Affected packages\n\n- `Flask-Security <= 5.8.0`\n\n## Remediation\n\nUpgrade to a patched release:\n\n- `Flask-Security 5.8.1`","depth":"sunlit","depthScore":26,"depthScoreParts":{"impact":25.9,"likelihood":0,"exploitation":0,"ransomware":0},"changes":[]}