Open Redirect in Flask Applications
Flask's redirect() function accepts any URL, and Flask-Login's @login_required decorator uses a ?next= parameter for post-login redirects. Without explicit validation, both mechanisms allow attackers to redirect users to malicious external sites after interacting with your trusted Flask application.
Scan Your Flask AppHow Open Redirect Manifests in Flask
The most common open redirect in Flask occurs with Flask-Login. When an unauthenticated user accesses a protected page, Flask-Login redirects them to the login page with ?next=/protected-page. After login, the app calls redirect(request.args.get('next')). If next contains an external URL, the redirect goes off-site. Custom Flask views that accept a redirect URL from the query string or form data are also vulnerable. A pattern like return redirect(request.form['redirect_to']) sends users wherever the form dictates. Flask blueprints that implement their own auth flows often duplicate this vulnerability independently. Each blueprint may have its own login route with its own next parameter handling. URL shortener or link tracking features built with Flask that store redirect destinations without validation create persistent open redirects — every visit to the short URL redirects to the attacker-controlled destination.
Real-World Impact
A Flask application using Flask-Login had a /login?next= parameter that accepted external URLs. An attacker distributed links to https://trusted-app.com/login?next=https://evil-clone.com in a phishing campaign. After users logged in to the real site, they were redirected to the clone which displayed "please verify your identity" and captured their MFA codes. A Flask-based URL shortener validated URL format but not destination. An attacker created short links pointing to malware download pages, making them appear to originate from the trusted shortener domain. The links bypassed email security filters that trusted the shortener's domain.
Step-by-Step Fix
Create a safe redirect utility for Flask
Build a function that validates redirect URLs and blocks external destinations.
# utils/safe_redirect.py
from urllib.parse import urlparse, urljoin
from flask import request
def is_safe_url(target):
"""Check if target URL is safe for redirect (same host)."""
ref_url = urlparse(request.host_url)
test_url = urlparse(urljoin(request.host_url, target))
return (
test_url.scheme in ('http', 'https') and
ref_url.netloc == test_url.netloc
)
def get_safe_redirect(target, default='/'):
"""Return target if safe, otherwise return default."""
if target and is_safe_url(target):
return target
return defaultSecure Flask-Login redirect flow
Validate the next parameter after Flask-Login's @login_required redirects users to the login page.
from flask import Flask, request, redirect, url_for, render_template
from flask_login import login_user, login_required
from utils.safe_redirect import get_safe_redirect
app = Flask(__name__)
@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
# ... validate credentials ...
login_user(user)
# UNSAFE: redirect(request.args.get('next', '/'))
# SAFE:
next_page = get_safe_redirect(
request.args.get('next'),
default=url_for('dashboard')
)
return redirect(next_page)
return render_template('login.html')
@app.route('/dashboard')
@login_required
def dashboard():
return render_template('dashboard.html')Validate redirects in blueprint auth flows
Apply the same validation in every blueprint that handles authentication.
# blueprints/admin/routes.py
from flask import Blueprint, request, redirect, url_for
from utils.safe_redirect import get_safe_redirect
admin_bp = Blueprint('admin', __name__, url_prefix='/admin')
@admin_bp.route('/login', methods=['GET', 'POST'])
def admin_login():
if request.method == 'POST':
# ... authenticate admin ...
next_page = get_safe_redirect(
request.args.get('next'),
default=url_for('admin.dashboard')
)
return redirect(next_page)
return render_template('admin/login.html')
# Apply to every blueprint that has its own auth flowPrevention Best Practices
1. Validate the next parameter in Flask-Login flows using url parsing. 2. Only allow relative paths for redirect destinations. 3. Use a helper function for all redirects that strips external URLs. 4. Validate stored redirect URLs (short links, webhook callbacks) at creation time. 5. Never redirect to request.referrer without validation.
How to Test
1. Visit a @login_required page while logged out, then modify the ?next= parameter to https://evil.com before logging in. 2. Try //evil.com, /\evil.com, and javascript:alert(1) as next values. 3. Search your codebase for redirect(request.args and redirect(request.form to find unvalidated redirects. 4. Check Flask-Login configuration for any custom unauthorized_handler that redirects without validation. 5. Use Vibe App Scanner to automatically detect open redirect vulnerabilities in your Flask application.
Frequently Asked Questions
Does Flask-Login prevent open redirects?
No. Flask-Login sets the next parameter to the page the user was trying to access, but it does not validate the next parameter when you read it back in your login view. You must validate it yourself using URL parsing before calling redirect(). The is_safe_url() pattern from the Flask security documentation is the recommended approach.
Is checking for http:// prefix enough to block open redirects?
No. Attackers bypass prefix checks with protocol-relative URLs (//evil.com), backslash tricks (/\evil.com), and URL encoding (%2F%2Fevil.com). Always parse the URL and compare the resulting hostname against your application's host, rather than doing string-level checks.
Can Flask's url_for() be exploited for open redirects?
url_for() generates URLs for your application's routes, so it cannot produce external URLs by itself. However, if you pass a _external=True parameter and the SERVER_NAME is misconfigured, or if you concatenate url_for() output with user input, vulnerabilities can arise. Use url_for() for generating redirect targets and avoid string concatenation with user input.
Related Security Resources
Is Your Flask App Vulnerable to Open Redirect?
VAS automatically scans for open redirect vulnerabilities in Flask applications and provides step-by-step remediation guidance with code examples.
Scans from $5, results in minutes. Get actionable fixes tailored to your Flask stack.