|
9 | 9 | from django.contrib.auth.models import User |
10 | 10 | from django.test import TestCase |
11 | 11 | from django.core import mail |
| 12 | +from django.core.exceptions import SuspiciousOperation |
12 | 13 | from django.core.urlresolvers import reverse |
13 | 14 | from django.http import QueryDict |
14 | 15 |
|
@@ -69,6 +70,44 @@ def test_email_found_custom_from(self): |
69 | 70 | self.assertEqual(len(mail.outbox), 1) |
70 | 71 | self.assertEqual("staffmember@example.com", mail.outbox[0].from_email) |
71 | 72 |
|
| 73 | + def test_admin_reset(self): |
| 74 | + "If the reset view is marked as being for admin, the HTTP_HOST header is used for a domain override." |
| 75 | + response = self.client.post('/admin_password_reset/', |
| 76 | + {'email': 'staffmember@example.com'}, |
| 77 | + HTTP_HOST='adminsite.com' |
| 78 | + ) |
| 79 | + self.assertEqual(response.status_code, 302) |
| 80 | + self.assertEqual(len(mail.outbox), 1) |
| 81 | + self.assertTrue("http://adminsite.com" in mail.outbox[0].body) |
| 82 | + self.assertEqual(settings.DEFAULT_FROM_EMAIL, mail.outbox[0].from_email) |
| 83 | + |
| 84 | + def test_poisoned_http_host(self): |
| 85 | + "Poisoned HTTP_HOST headers can't be used for reset emails" |
| 86 | + # This attack is based on the way browsers handle URLs. The colon |
| 87 | + # should be used to separate the port, but if the URL contains an @, |
| 88 | + # the colon is interpreted as part of a username for login purposes, |
| 89 | + # making 'evil.com' the request domain. Since HTTP_HOST is used to |
| 90 | + # produce a meaningful reset URL, we need to be certain that the |
| 91 | + # HTTP_HOST header isn't poisoned. This is done as a check when get_host() |
| 92 | + # is invoked, but we check here as a practical consequence. |
| 93 | + def test_host_poisoning(): |
| 94 | + self.client.post('/password_reset/', |
| 95 | + {'email': 'staffmember@example.com'}, |
| 96 | + HTTP_HOST='www.example:dr.frankenstein@evil.tld' |
| 97 | + ) |
| 98 | + self.assertRaises(SuspiciousOperation, test_host_poisoning) |
| 99 | + self.assertEqual(len(mail.outbox), 0) |
| 100 | + |
| 101 | + def test_poisoned_http_host_admin_site(self): |
| 102 | + "Poisoned HTTP_HOST headers can't be used for reset emails on admin views" |
| 103 | + def test_host_poisoning(): |
| 104 | + self.client.post('/admin_password_reset/', |
| 105 | + {'email': 'staffmember@example.com'}, |
| 106 | + HTTP_HOST='www.example:dr.frankenstein@evil.tld' |
| 107 | + ) |
| 108 | + self.assertRaises(SuspiciousOperation, test_host_poisoning) |
| 109 | + self.assertEqual(len(mail.outbox), 0) |
| 110 | + |
72 | 111 | def _test_confirm_start(self): |
73 | 112 | # Start by creating the email |
74 | 113 | response = self.client.post('/password_reset/', {'email': 'staffmember@example.com'}) |
|
0 commit comments