diff --git a/datasette/utils/asgi.py b/datasette/utils/asgi.py index 35f243b6..c68f1a27 100644 --- a/datasette/utils/asgi.py +++ b/datasette/utils/asgi.py @@ -330,9 +330,11 @@ async def asgi_send_html(send, html, status=200, headers=None): async def asgi_send_redirect(send, location, status=302): - # Prevent open redirect vulnerability: strip multiple leading slashes - # //example.com would be interpreted as a protocol-relative URL (e.g., https://example.com/) - location = re.sub(r"^/+", "/", location) + # Prevent open redirect vulnerability: collapse leading slashes and + # backslashes into a single slash. Browsers treat a backslash as a slash, + # so //example.com, /\example.com and /\/example.com would all otherwise be + # interpreted as a protocol-relative URL (e.g. https://example.com/). + location = re.sub(r"^[/\\]+", "/", location) await asgi_send( send, "", diff --git a/tests/test_custom_pages.py b/tests/test_custom_pages.py index 39a4c06b..3fadb587 100644 --- a/tests/test_custom_pages.py +++ b/tests/test_custom_pages.py @@ -99,8 +99,18 @@ def test_custom_route_pattern_404(custom_pages_client): assert ">Oh no