diff -r e545a92e9079 django/conf/global_settings.py
a
|
b
|
|
521 | 521 | |
522 | 522 | # The list of directories to search for fixtures |
523 | 523 | FIXTURE_DIRS = () |
| 524 | |
| 525 | #Default url to redirect to to avoid Open Redirect issues. See: #13751 |
| 526 | REDIRECT_FAILURE_URL = "/" |
diff -r e545a92e9079 django/http/__init__.py
a
|
b
|
|
3 | 3 | from Cookie import BaseCookie, SimpleCookie, CookieError |
4 | 4 | from pprint import pformat |
5 | 5 | from urllib import urlencode |
6 | | from urlparse import urljoin |
| 6 | from urlparse import urljoin, urlparse |
| 7 | import logging |
7 | 8 | try: |
8 | 9 | # The mod_python version is more efficient, so try importing it first. |
9 | 10 | from mod_python.util import parse_qsl |
… |
… |
|
432 | 433 | class HttpResponseRedirect(HttpResponse): |
433 | 434 | status_code = 302 |
434 | 435 | |
435 | | def __init__(self, redirect_to): |
| 436 | def __init__(self, redirect_to, whitelist=[], fallback_to=None): |
436 | 437 | HttpResponse.__init__(self) |
437 | 438 | self['Location'] = iri_to_uri(redirect_to) |
| 439 | |
| 440 | if urlparse(self['Location']).scheme: |
| 441 | effective_whitelist = whitelist + getattr(settings, 'REDIRECT_WHITELIST', []) |
| 442 | |
| 443 | matched = False |
| 444 | |
| 445 | for pattern in effective_whitelist: |
| 446 | matched = re.compile(pattern).match(self['Location']) |
| 447 | |
| 448 | if matched: |
| 449 | break |
| 450 | |
| 451 | if not matched: |
| 452 | logging.warn("Found open redirect attack to %s", self['Location']) |
| 453 | |
| 454 | self['Location'] = fallback_to or settings.REDIRECT_FAILURE_URL |
438 | 455 | |
439 | 456 | class HttpResponsePermanentRedirect(HttpResponse): |
440 | 457 | status_code = 301 |
diff -r e545a92e9079 docs/ref/request-response.txt
a
|
b
|
|
561 | 561 | |
562 | 562 | .. class:: HttpResponseRedirect |
563 | 563 | |
564 | | The constructor takes a single argument -- the path to redirect to. This |
| 564 | The constructor takes a single required argument -- the path to redirect to. This |
565 | 565 | can be a fully qualified URL (e.g. ``'http://www.yahoo.com/search/'``) or an |
566 | | absolute URL with no domain (e.g. ``'/search/'``). Note that this returns |
567 | | an HTTP status code 302. |
| 566 | absolute URL with no domain (e.g. ``'/search/'``). |
| 567 | |
| 568 | Two optional parameters are ``whitelist`` and ``fallback_to`` |
| 569 | that are used to avoid the Open Redirect security issue. (see: http://www.google.com/support/webmasters/bin/answer.py?answer=171297 ) |
| 570 | The ``whitelist`` param is a list of regular expressions for whitelisted urls. |
| 571 | A whitelist can also be defined in the :setting:`REDIRECT_WHITELIST` setting. |
| 572 | Additionally, the ``fallback_to`` param is an optional URL to fallback to if |
| 573 | none of the ``whitelist`` patterns match. You can also set the :setting:REDIRECT_FAILURE_URL |
| 574 | setting to specify your own default fallback. By default this is set to "/". |
| 575 | |
| 576 | Note that this returns an HTTP status code 302. |
568 | 577 | |
569 | 578 | .. class:: HttpResponsePermanentRedirect |
570 | 579 | |
diff -r e545a92e9079 tests/regressiontests/httpwrappers/tests.py
a
|
b
|
|
1 | 1 | import copy |
2 | 2 | import pickle |
3 | 3 | import unittest |
4 | | from django.http import QueryDict, HttpResponse, CompatCookie, BadHeaderError |
| 4 | from django.http import QueryDict, HttpResponse, CompatCookie, BadHeaderError,\ |
| 5 | HttpResponseRedirect |
| 6 | from django.conf import settings |
| 7 | |
5 | 8 | |
6 | 9 | class QueryDictTests(unittest.TestCase): |
7 | 10 | def test_missing_key(self): |
… |
… |
|
254 | 257 | c2 = CompatCookie() |
255 | 258 | c2.load(c.output()) |
256 | 259 | self.assertEqual(c['test'].value, c2['test'].value) |
| 260 | |
| 261 | |
| 262 | class HttpResponseRedirectTests(unittest.TestCase): |
| 263 | def test_open_redirect(self): |
| 264 | self.assertEqual('/test', HttpResponseRedirect('/test')['Location']) |
| 265 | self.assertEqual(settings.REDIRECT_FAILURE_URL, HttpResponseRedirect('http://djangoproject.com')['Location']) |
| 266 | |
| 267 | r = HttpResponseRedirect('http://djangoproject.com', whitelist=[r'.*']) |
| 268 | self.assertEqual('http://djangoproject.com', r['Location']) |
| 269 | |
| 270 | setattr(settings, 'REDIRECT_WHITELIST', [r'.*']) |
| 271 | self.assertEqual('http://djangoproject.com', HttpResponseRedirect('http://djangoproject.com')['Location']) |