Skip to content

Commit 2545222

Browse files
committed
Merge branch 'GHSA-v6wp-4m6f-gcjg' into master
This patch fixes an open redirect vulnerability bug in `aiohttp.web_middlewares.normalize_path_middleware` by making sure that there's at most one slash at the beginning of the `Location` header value. Refs: * https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html * GHSA-v6wp-4m6f-gcjg
2 parents f2afa2f + d2f9a83 commit 2545222

File tree

3 files changed

+42
-0
lines changed

3 files changed

+42
-0
lines changed

Diff for: CHANGES/5497.bugfix

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
**(SECURITY BUG)** Started preventing open redirects in the
2+
``aiohttp.web.normalize_path_middleware`` middleware. For
3+
more details, see
4+
https://github.com/aio-libs/aiohttp/security/advisories/GHSA-v6wp-4m6f-gcjg.
5+
6+
Thanks to `Beast Glatisant <https://github.com/g147>`__ for
7+
finding the firstinstance of this issue and `Jelmer Vernooij
8+
<https://jelmer.uk/>`__ for reporting and tracking it down
9+
in aiohttp.

Diff for: aiohttp/web_middlewares.py

+1
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ async def impl(request: Request, handler: _Handler) -> StreamResponse:
108108
paths_to_check.append(merged_slashes[:-1])
109109

110110
for path in paths_to_check:
111+
path = re.sub("^//+", "/", path) # SECURITY: GHSA-v6wp-4m6f-gcjg
111112
resolves, request = await _check_request_resolves(request, path)
112113
if resolves:
113114
raise redirect_class(request.raw_path + query)

Diff for: tests/test_web_middleware.py

+32
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,38 @@ async def test_cannot_remove_and_add_slash(self) -> None:
361361
with pytest.raises(AssertionError):
362362
web.normalize_path_middleware(append_slash=True, remove_slash=True)
363363

364+
@pytest.mark.parametrize(
365+
["append_slash", "remove_slash"],
366+
[
367+
(True, False),
368+
(False, True),
369+
(False, False),
370+
],
371+
)
372+
async def test_open_redirects(
373+
self, append_slash: bool, remove_slash: bool, aiohttp_client: Any
374+
) -> None:
375+
async def handle(request: web.Request) -> web.StreamResponse:
376+
pytest.fail(
377+
msg="Security advisory 'GHSA-v6wp-4m6f-gcjg' test handler "
378+
"matched unexpectedly",
379+
pytrace=False,
380+
)
381+
382+
app = web.Application(
383+
middlewares=[
384+
web.normalize_path_middleware(
385+
append_slash=append_slash, remove_slash=remove_slash
386+
)
387+
]
388+
)
389+
app.add_routes([web.get("/", handle), web.get("/google.com", handle)])
390+
client = await aiohttp_client(app, server_kwargs={"skip_url_asserts": True})
391+
resp = await client.get("//google.com", allow_redirects=False)
392+
assert resp.status == 308
393+
assert resp.headers["Location"] == "/google.com"
394+
assert resp.url.query == URL("//google.com").query
395+
364396

365397
async def test_bug_3669(aiohttp_client: Any):
366398
async def paymethod(request):

0 commit comments

Comments
 (0)