python.flask.security.open-redirect.open-redirect
Community Favorite
semgrep
Author
48,300
Download Count*
License
Data from request is passed to redirect(). This is an open redirect and could be exploited. Consider using 'url_for()' to generate links to known locations. If you must use a URL to unknown pages, consider using 'urlparse()' or similar and checking if the 'netloc' property is the same as your site's host name. See the references for more information.
Run Locally
Run in CI
Defintion
rules:
- id: open-redirect
patterns:
- pattern-inside: |
@$APP.route(...)
def $X(...):
...
- pattern-not-inside: |
@$APP.route(...)
def $X(...):
...
if <... werkzeug.urls.url_parse($V) ...>:
...
- pattern-either:
- pattern: flask.redirect(<... flask.request.$W.get(...) ...>, ...)
- pattern: flask.redirect(<... flask.request.$W[...] ...>, ...)
- pattern: flask.redirect(<... flask.request.$W(...) ...>, ...)
- pattern: flask.redirect(<... flask.request.$W ...>, ...)
- pattern: |
$V = flask.request.$W.get(...)
...
flask.redirect(<... $V ...>, ...)
- pattern: |
$V = flask.request.$W[...]
...
flask.redirect(<... $V ...>, ...)
- pattern: |
$V = flask.request.$W(...)
...
flask.redirect(<... $V ...>, ...)
- pattern: |
$V = flask.request.$W
...
flask.redirect(<... $V ...>, ...)
- pattern-not: flask.redirect(flask.request.path)
- pattern-not: flask.redirect(flask.request.path + ...)
- pattern-not: flask.redirect(f"{flask.request.path}...")
message: Data from request is passed to redirect(). This is an open redirect and
could be exploited. Consider using 'url_for()' to generate links to known
locations. If you must use a URL to unknown pages, consider using
'urlparse()' or similar and checking if the 'netloc' property is the same
as your site's host name. See the references for more information.
metadata:
cwe:
- "CWE-601: URL Redirection to Untrusted Site ('Open Redirect')"
owasp:
- A01:2021 - Broken Access Control
references:
- https://flask-login.readthedocs.io/en/latest/#login-example
- https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html#dangerous-url-redirect-example-1
- https://docs.python.org/3/library/urllib.parse.html#url-parsing
category: security
technology:
- flask
subcategory:
- audit
likelihood: LOW
impact: MEDIUM
confidence: LOW
license: Commons Clause License Condition v1.0[LGPL-2.1-only]
vulnerability_class:
- Open Redirect
languages:
- python
severity: ERROR
Examples
open-redirect.py
from flask import request, redirect
from flask import Flask, redirect, request, url_for
from werkzeug.urls import url_parse
app = Flask(__name__)
@app.route("open_redirect/")
def open_redirect():
# ruleid: open-redirect
url = request.args.get("url")
print("something")
return redirect(url)
@app.route("not_open_redirect/")
def not_open_redirect():
page = request.args.get("page")
if page == "about":
# ok: open-redirect
url = "/about/"
return redirect(url)
elif page == "test":
# ok: open-redirect
redirect(f"{request.path}/")
else:
# ok: open-redirect
redirect(request.path + "?failed")
@app.route("filter")
def filter():
# ok: open-redirect
next_page = request.args.get('next')
if not next_page or url_parse(next_page).netloc != '':
next_page = url_for('main.index')
return redirect(next_page)
# cf. https://github.com/mideind/Netskrafl/blob/2e1933ad0710a4425c319fde3b92b2a70729ed80/netskrafl.py#L1712
@app.route("/userprefs", methods=["GET", "POST"])
@auth_required()
def userprefs():
""" Handler for the user preferences page """
user = current_user()
uf = UserForm()
err = dict()
# The URL to go back to, if not main.html
# ruleid: open-redirect
from_url = request.args.get("from", None)
if request.method == "GET":
# Entering the form for the first time: load the user data
uf.init_from_user(user)
elif request.method == "POST":
# Attempting to submit modified data: retrieve it and validate
uf.init_from_form(request.form)
err = uf.validate()
if not err:
# All is fine: store the data back in the user entity
uf.store(user)
return redirect(from_url or url_for("main"))
# Render the form with the current data and error messages, if any
return render_template("userprefs.html", uf=uf, err=err, from_url=from_url)
Short Link: https://sg.run/kXe2