Skip to content

Commit a4639c7

Browse files
author
Max Schaefer
committed
Update qhelp to mention solution using urlparse.
1 parent 17e3a45 commit a4639c7

File tree

3 files changed

+47
-20
lines changed

3 files changed

+47
-20
lines changed

python/ql/src/Security/CWE-601/UrlRedirect.qhelp

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -39,26 +39,39 @@ and check that the user input is in that list:
3939

4040
<p>
4141
Often this is not possible, so an alternative is to check that the target URL does not
42-
specify an explicit host name. For example, the Django framework provides a
43-
function <code>url_has_allowed_host_and_scheme</code> that can be used to check that a
44-
URL is safe to redirect to, as shown in the following example:
42+
specify an explicit host name. For example, you can use the <code>urlparse</code> function
43+
from the Python standard library to parse the URL and check that the <code>netloc</code>
44+
attribute is empty.
45+
</p>
46+
47+
<p>
48+
Note, however, that many browsers accept backslash characters (<code>\</code>) as equivalent
49+
to forward slash characters (<code>/</code>) in URLs, but the <code>urlparse</code> function
50+
does not. To account for this, you can first replace all backslashes with forward slashes,
51+
as shown in the following example:
4552
</p>
4653

4754
<sample src="examples/redirect_good2.py"/>
4855

4956
<p>
50-
Note that many browsers accept backslash characters (<code>\</code>) as equivalent to
51-
forward slash characters (<code>/</code>) in URLs, so it is important to account for this
52-
when validating the URL, for example by first replacing all backslashes with forward
53-
slashes. Django's <code>url_has_allowed_host_and_scheme</code> function
54-
does this automatically, but other libraries may not.
57+
For Django application, you can use the function <code>url_has_allowed_host_and_scheme</code>
58+
to check that a URL is safe to redirect to, as shown in the following example:
59+
</p>
60+
61+
<sample src="examples/redirect_good3.py"/>
62+
63+
<p>
64+
Note that <code>url_has_allowed_host_and_scheme</code> handles backslashes correctly, so no
65+
additional processing is required.
5566
</p>
5667

5768
</example>
5869

5970
<references>
6071
<li>OWASP: <a href="https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html">
6172
XSS Unvalidated Redirects and Forwards Cheat Sheet</a>.</li>
73+
<li>Python standard library: <a href="https://docs.python.org/3/library/urllib.parse.html">
74+
urllib.parse</a>.</li>
6275
</references>
6376

6477
</qhelp>
Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
1-
from django.http import HttpResponseRedirect
2-
from django.shortcuts import redirect
3-
from django.utils.http import url_has_allowed_host_and_scheme
4-
from django.views import View
1+
from flask import Flask, request, redirect
2+
from urllib.parse import urlparse
53

6-
class RedirectView(View):
7-
def get(self, request, *args, **kwargs):
8-
target = request.GET.get('target', '')
9-
if url_has_allowed_host_and_scheme(target, allowed_hosts=None):
10-
return HttpResponseRedirect(target)
11-
else:
12-
# ignore the target and redirect to the home page
13-
return redirect('/')
4+
app = Flask(__name__)
5+
6+
@app.route('/')
7+
def hello():
8+
target = request.args.get('target', '')
9+
target = target.replace('\\', '')
10+
if not urlparse(target).netloc:
11+
# relative path, safe to redirect
12+
return redirect(target, code=302)
13+
# ignore the target and redirect to the home page
14+
return redirect('/', code=302)
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
from django.http import HttpResponseRedirect
2+
from django.shortcuts import redirect
3+
from django.utils.http import url_has_allowed_host_and_scheme
4+
from django.views import View
5+
6+
class RedirectView(View):
7+
def get(self, request, *args, **kwargs):
8+
target = request.GET.get('target', '')
9+
if url_has_allowed_host_and_scheme(target, allowed_hosts=None):
10+
return HttpResponseRedirect(target)
11+
else:
12+
# ignore the target and redirect to the home page
13+
return redirect('/')

0 commit comments

Comments
 (0)