Skip to content

Commit 7eead06

Browse files
committed
- Fix open redirect issue in redirect handling
1 parent c49ddb0 commit 7eead06

File tree

3 files changed

+17
-3
lines changed

3 files changed

+17
-3
lines changed

CHANGES.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ Change Log
44
2.6.0 (unreleased)
55
------------------
66

7+
- Fix open redirect issue in `Cookie Auth Helper` redirect handling
8+
79
- Add support for Python 3.9.
810

911

src/Products/PluggableAuthService/plugins/CookieAuthHelper.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@
2828
import six
2929
from six.moves.urllib.parse import quote
3030
from six.moves.urllib.parse import unquote
31+
from six.moves.urllib.parse import urlparse
32+
from six.moves.urllib.parse import urlunparse
3133

3234
from AccessControl.class_init import InitializeClass
3335
from AccessControl.Permissions import view
@@ -225,6 +227,12 @@ def unauthorized(self):
225227
# in an endless redirect loop.
226228
return 0
227229

230+
# Sanitize the return URL ``came_from`` and only allow local URLs
231+
# to prevent an open exploitable redirect issue
232+
if came_from:
233+
parsed = urlparse(came_from)
234+
came_from = urlunparse(('', '') + parsed[2:])
235+
228236
if '?' in url:
229237
sep = '&'
230238
else:

src/Products/PluggableAuthService/plugins/tests/test_CookieAuthHelper.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,8 @@ def test_extractCredentials_with_deleted_cookie(self):
128128
def test_challenge(self):
129129
rc, root, folder, object = self._makeTree()
130130
response = FauxCookieResponse()
131-
testURL = 'http://test'
131+
testPath = '/some/path'
132+
testURL = 'http://test' + testPath
132133
request = FauxRequest(RESPONSE=response, URL=testURL,
133134
ACTUAL_URL=testURL)
134135
root.REQUEST = request
@@ -138,7 +139,9 @@ def test_challenge(self):
138139
helper.challenge(request, response)
139140
self.assertEqual(response.status, 302)
140141
self.assertEqual(len(response.headers), 3)
141-
self.assertTrue(response.headers['Location'].endswith(quote(testURL)))
142+
loc = response.headers['Location']
143+
self.assertTrue(loc.endswith(quote(testPath)))
144+
self.assertNotIn(testURL, loc)
142145
self.assertEqual(response.headers['Cache-Control'], 'no-cache')
143146
self.assertEqual(response.headers['Expires'],
144147
'Sat, 01 Jan 2000 00:00:00 GMT')
@@ -159,8 +162,9 @@ def test_challenge_with_vhm(self):
159162
self.assertEqual(response.status, 302)
160163
self.assertEqual(len(response.headers), 3)
161164
loc = response.headers['Location']
162-
self.assertTrue(loc.endswith(quote(actualURL)))
165+
self.assertTrue(loc.endswith(quote('/xxx')))
163166
self.assertFalse(loc.endswith(quote(vhm)))
167+
self.assertNotIn(actualURL, loc)
164168
self.assertEqual(response.headers['Cache-Control'], 'no-cache')
165169
self.assertEqual(response.headers['Expires'],
166170
'Sat, 01 Jan 2000 00:00:00 GMT')

0 commit comments

Comments
 (0)