diff --git a/soapbox/admin.py b/soapbox/admin.py index 3b2d53b..4c4bc14 100644 --- a/soapbox/admin.py +++ b/soapbox/admin.py @@ -129,7 +129,9 @@ def login(): session["uid"] = user.id csrf_token() nxt = request.form.get("next") or url_for("admin.index") - if not nxt.startswith("/"): + # Kun stier på dette nettstedet: "//evil.com" og "/\\evil.com" + # tolkes som skjema-relative URL-er av nettlesere. + if not nxt.startswith("/") or nxt.startswith("//") or "\\" in nxt: nxt = url_for("admin.index") return redirect(nxt) flash("Feil brukernavn eller passord.", "error") diff --git a/tests/test_app.py b/tests/test_app.py index a169890..03f399f 100644 --- a/tests/test_app.py +++ b/tests/test_app.py @@ -109,6 +109,18 @@ def test_guest_flow_and_isolation(client, cfg): assert client.get("/nytt-innlegg/").status_code == 404 +def test_login_next_no_open_redirect(client): + for bad in ("//evil.com", "/\\evil.com", "https://evil.com"): + r = client.post( + "/admin/login", data={"username": "eier", "password": "hemmelig123", "next": bad} + ) + assert r.headers["Location"] == "/admin/", bad + r = client.post( + "/admin/login", data={"username": "eier", "password": "hemmelig123", "next": "/admin/users"} + ) + assert r.headers["Location"] == "/admin/users" + + def test_csrf_enforced(client): login(client) assert client.post("/admin/new", data={}).status_code == 403