Jun 24, 2026·8 min read·2 visits
A parser differential between Python's urlsplit() and web browsers allows attackers to bypass subdomain redirect validation in Flask-Security using backslash-based host strings, leading to open redirect attacks.
An open redirect vulnerability exists in Flask-Security versions up to and including 5.8.0. This flaw allows remote, unauthenticated attackers to perform open redirects by exploiting a parser differential between Python's standard library urlsplit() function and modern web browsers when subdomain redirection is allowed.
The vulnerability resides within the redirection validation mechanism of the Flask-Security integration package. This library offers security features for Flask applications, including user registration, authentication, and password recovery. A common architectural pattern for these applications involves redirecting users to their originally requested path after completing an authentication flow, such as logging in or logging out. The attack surface is exposed through user-controlled parameters, typically named 'next' or 'redirect', which accept destination URLs.
Under specific configurations, such as when SECURITY_REDIRECT_ALLOW_SUBDOMAINS is set to True, the application tries to allow redirects only to authorized subdomains of the main server domain. The validation logic relies on the flask_security.utils.validate_redirect_url() function to verify that the target URL belongs to a trusted domain before issuing the HTTP 302 redirect header. When the application contains this specific setting, it trusts the validation utility to filter out malicious destinations.
This implementation is vulnerable to a parser differential attack that exploits discrepancies between the application server's backend parsing engine and the victim's frontend web browser. Successful exploitation of this vulnerability yields an open redirect bypass, classified under CWE-601. Unauthenticated remote attackers can leverage this flaw to redirect victims to malicious, lookalike domains to capture user credentials or execute secondary web-based attacks.
The underlying flaw is located in Python's standard library parser function urllib.parse.urlsplit(), which is used directly by Flask-Security's validate_redirect_url(). To decide whether an input URL represents an authorized subdomain, the validation utility extracts the netloc component of the URL. The application then checks if the extracted hostname ends with the configured valid domain, such as '.whitelist.com'.
When an attacker provides a URL containing a backslash ('\') within the authority portion of the URL structure, such as 'http://evil.com\\\\.whitelist.com', Python's parser evaluates the string differently than modern web standards. Specifically, RFC 3986 specifies rules for URL parsing that standard libraries implement with varying degrees of strictness. Python's urlsplit() treats the backslash character as a valid character within the netloc authority block rather than a path separator.
Consequently, the utility parses the entire string 'evil.com\\.whitelist.com' as the hostname. Because this parsed string ends with the suffix '.whitelist.com', the validation logic evaluates the entire host as a legitimate subdomain of the whitelisted domain. The validation function returns True, indicating the URL is safe for redirection, and the backend server issues a 302 Found response containing this URL in the Location header.
Once the client browser receives the HTTP response, the threat materializes. Modern web browsers, conforming to the WHATWG Living Standard for URLs, normalize backslashes inside authority headers to forward slashes ('/') before executing the network request. The browser transforms the location target to 'http://evil.com/.whitelist.com'. The browser then parses 'evil.com' as the true target host and '.whitelist.com' as the path, routing the user to the attacker-controlled server.
To illustrate the architectural defect, we examine the vulnerable code path that processes the redirection check. The validation utility processes user input before transmitting the HTTP redirection header to the client.
# Vulnerable implementation logic within validate_redirect_url()
from urllib.parse import urlsplit
def validate_redirect_url(url):
# The input string is parsed using Python's standard utility
parsed = urlsplit(url)
netloc = parsed.netloc
# Subdomain validation checks only if the parsed netloc ends with the base domain
if SECURITY_REDIRECT_ALLOW_SUBDOMAINS:
base_domain = app.config["SERVER_NAME"] # e.g., 'whitelist.com'
if netloc.endswith("." + base_domain):
return True # Returns True because 'evil.com\\\\.whitelist.com' ends with '.whitelist.com'
return FalseThe fix introduced in Flask-Security 5.8.1 changes the handling of authority components by strictly sanitizing characters before running the validation check or by rejecting URLs containing illegal characters in the authority portion. The following conceptual implementation represents the hardened validation logic.
# Hardened validation logic introduced to counter the parser differential
from urllib.parse import urlsplit
def validate_redirect_url_patched(url):
# First, check for characters that cause parser differentials between Python and browsers
if "\\\\" in url or "%5c" in url.lower():
# Explicitly reject backslashes to prevent normalization bypasses
return False
parsed = urlsplit(url)
netloc = parsed.netloc
# Standard subdomain validation continues after safety checks
if SECURITY_REDIRECT_ALLOW_SUBDOMAINS:
base_domain = app.config["SERVER_NAME"]
if netloc.endswith("." + base_domain):
return True
return FalseThe defensive correction ensures that any occurrence of raw or encoded backslashes within the target URL results in immediate validation failure. By blocking these characters, the application prevents modern browsers from receiving a payload that they would normalize into an external destination. The patch effectively aligns the server-side host parsing model with the client-side browser execution model.
The physical routing of this attack is demonstrated in the diagram below, showing how the backend server and client browser process the path differently.
The exploitation flow requires minimal preparation and can be executed by an unauthenticated remote attacker. The threat actor first identifies a target application running a vulnerable version of Flask-Security with subdomain redirects enabled. The attacker then constructs a malicious URL that points to their infrastructure while appending a suffix that mimics the target host domain.
Using either a raw backslash or a URL-encoded representation ('%5C'), the attacker formats the target URL. An example of a crafted payload is 'http://attacker-controlled.com%5C.target-domain.com/login'. This URL is set as the value for the redirection query parameter, which is commonly named 'next' or 'redirect' in authentication forms.
The attacker distributes this crafted link to target users using phishing emails or social engineering vectors. When a victim clicks the link, they are directed to the legitimate login interface of the target application. Once the victim completes the authentication process, the application validates the redirection parameter, accepts it as safe, and responds with a 302 Found status containing the payload. The client's web browser processes the header, converts the backslash into a forward slash, and forwards the session to the attacker's server.
The primary impact of this open redirect vulnerability is the facilitation of highly convincing credential-harvesting campaigns. Because the initial domain in the address bar is the trusted, legitimate host, the user undergoes normal authentication. The subsequent seamless redirection to an external lookalike page can deceive security-conscious users into re-entering their credentials or executing secondary malicious actions on the destination server.
This vulnerability receives a CVSS v3.1 base score of 4.7. The rating reflects the requirement for user interaction, as a victim must initiate the connection by clicking the maliciously crafted link. The scope is designated as changed (S:C) because the security authority shifts from the trusted domain to an untrusted external entity. Confidentiality is rated low (C:L) because OAuth authorization codes, session tokens, or other sensitive parameters appended to the redirect URL can be leaked to the external server's access logs.
While open redirects are often considered low-severity issues, their utility in multi-stage attack chains is significant. Security teams should treat this vulnerability as a high-priority fix in environments where single sign-on (SSO) or OAuth workflows are configured. If the application forwards authentication state tokens via URL parameters, this bypass directly threatens the integrity of those active sessions.
The most effective and permanent resolution is upgrading Flask-Security to version 5.8.1 or higher. The maintainers implemented robust parsing filters in this version to eliminate parser differentials. When upgrading, deployment teams must ensure that transit dependencies such as Werkzeug are also updated to compatible versions.
If upgrading is not immediately possible, security teams must deploy defensive workarounds. The simplest administrative fix is setting SECURITY_REDIRECT_ALLOW_SUBDOMAINS = False in the application configuration. This setting restricts redirection exclusively to the exact domain defined in SERVER_NAME or local relative paths, thereby bypassing the vulnerable subdomain validation routines entirely.
Alternatively, developers can implement custom validation middleware to filter incoming redirection parameters. The following Python code snippet demonstrates an effective filtering strategy to reject unsafe inputs before Flask-Security processes them.
from flask import abort, request
def sanitize_redirects_middleware():
target = request.args.get("next", "")
# Intercept and reject any redirection parameters with path-normalization indicators
if "\\\\" in target or "2f" in target.lower() or "5c" in target.lower():
abort(400, "Insecure URL redirect requested.")To detect active exploitation attempts, operations teams should audit application server access logs. Standard queries should scan for requests to login or redirection endpoints containing backslashes or their hex-encoded equivalents within parameter values. Intrusion detection systems (IDS) should also run rules that flag outbound requests with HTTP 302 statuses redirecting to unrecognized destination servers containing backslashes.
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:N/A:N| Product | Affected Versions | Fixed Version |
|---|---|---|
Flask-Security pallets-eco | <= 5.8.0 | 5.8.1 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-601 |
| Attack Vector | Network (AV:N) |
| CVSS Score | 4.7 (Medium) |
| EPSS Score | N/A |
| Impact | Open Redirect |
| Exploit Status | Proof-of-Concept |
| KEV Status | Not Listed |
A web application accepts a user-controlled input that specifies a link to an external site, and uses that link in a Redirect.
A UNIX symbolic link following vulnerability exists in the provider cache installation mechanism of OpenTofu. This flaw allows an attacker with control over the repository files to write files outside of the intended workspace boundary during initialization.
An incorrect authorization vulnerability (CWE-863) in Snipe-IT versions prior to 8.6.0 allows authenticated, low-privileged users with granular 'users.edit' permissions to modify restricted user flags ('activated' and 'ldap_import') and merge high-privileged administrator accounts into standard user accounts. This allows an attacker to lock administrators out of the system or completely hijack administrator accounts.
An incomplete security patch for CVE-2026-24421 in phpMyFAQ allows authenticated low-privileged users to bypass role-based access controls. While the initial patch addressed missing authorization in the BackupController, it left four critical write-enabled endpoints vulnerable. This allows remote attackers with a valid low-privilege API token to perform unauthorized data modifications, creating categories, creating FAQs, updating FAQs, and injecting questions directly into the database.
An in-depth security audit of the skillctl command-line package manager revealed five critical and high-severity security vulnerabilities. The identified flaws span parameter-level command argument injection via the source_sha parameter, uncontrolled resource consumption (Denial of Service) through unnamed UNIX FIFOs and character devices, directory path traversal in the destination argument, commit-message trailer forgery via newline injection in skill names, and local credential exfiltration leveraging UNIX hardlinks. These vulnerabilities represent significant vectors for workstation compromise when executing agentic tasks in repositories containing untrusted files or pull requests. Remediation was introduced in version v0.1.3.
CVE-2026-48153 is a Server-Side Request Forgery (SSRF) vulnerability in the Budibase OAuth2 SDK prior to version 3.39.0. It allows authenticated low-privileged users to bypass outbound network security blacklists and send arbitrary requests to internal subnets or cloud metadata services.
The self-hosted Slack Nebula VPN control plane, nebula-mesh, stored high-privilege enrollment tokens in plaintext inside its SQLite database. This flaw allowed any adversary with read access to the database to retrieve pending tokens and enroll unauthorized hosts into the secure VPN mesh.