Open redirect
CWE-601OWASP A01:2021Bijgewerkt 31 augustus 20266 min leestijd
Een open redirect is een kwetsbaarheid waarbij een applicatie bezoekers doorstuurt naar een adres uit de invoer, zoals een returnUrl-parameter, zonder te controleren of dat adres binnen het eigen domein valt. Aanvallers misbruiken dat voor phishing en om OAuth-tokens te stelen. Veilig is een allowlist van relatieve paden of een sleutel die serverzijdig naar een vast pad wordt vertaald.
Vrijwel elke applicatie stuurt bezoekers wel eens door: na het inloggen terug naar de pagina waar iemand vandaan kwam, na het afrekenen naar een bevestiging. Waar die bestemming vandaan komt, staat meestal in een parameter als returnUrl of next. Neemt de applicatie die waarde ongecontroleerd over, dan bepaalt niet u maar de opsteller van de link waar uw bezoeker uitkomt.
Wat is een open redirect?
Een open redirect is een kwetsbaarheid waarbij een applicatie een bezoeker doorstuurt naar een adres dat volledig uit de invoer komt, zonder te controleren of dat adres binnen het eigen domein valt. De aanvaller stuurt geen code naar binnen en breekt niets open; hij leent alleen de goede naam van uw domein om iemand ergens anders te laten aankomen.
Vergelijk het met een wegwijzer in uw ontvangsthal waarop bezoekers zelf een bestemming mogen schrijven. Wie het bord leest, vertrouwt het omdat het in uw hal hangt, ook als er een heel ander gebouw op staat. De link die het slachtoffer krijgt, begint met uw domein, en dat is precies wat een mailfilter, een linkscanner en het oog van de ontvanger controleren.
De parameter heet in de praktijk returnUrl, next, redirect, url, dest of continue en is bijna altijd legitiem bedoeld: wie zonder sessie op een beveiligde pagina belandt, gaat naar het inlogscherm met de oorspronkelijke pagina in die parameter. Het probleem ontstaat pas wanneer de parameter een volledige URL (Uniform Resource Locator) mag bevatten in plaats van een pad binnen uw eigen applicatie.
Hoe werkt een open redirect-aanval?
Neem een inlogroute die de gebruiker na authenticatie terugstuurt naar de pagina uit de parameter.
Kwetsbaar:
app.post("/login", async (req, res) => {
const user = await authenticate(req.body.username, req.body.password);
if (!user) return res.status(401).render("login");
req.session.userId = user.id;
// De bestemming komt rechtstreeks uit de query string
res.redirect(req.query.returnUrl || "/dashboard");
});
De aanvaller stelt een link op die met uw eigen domein begint en daarom langs de meeste filters komt:
GET /login?returnUrl=https://app-example.attacker.test/sessie HTTP/1.1
Host: app.example.com
Het slachtoffer logt bij u in en wordt met een 302 doorgestuurd naar de aanvaller. Daar staat een kopie van uw inlogscherm met de melding dat de sessie is verlopen. Het slachtoffer typt zijn wachtwoord opnieuw, nu bij iemand anders.
De eerste reflex is een controle op het eerste teken: begint de waarde met een slash, dan zal het wel een pad binnen de applicatie zijn. Die aanname klopt niet, want een browser ontleedt een adres volgens meer regels dan een ontwikkelaar meestal in gedachten heeft:
//evil.exampleis geen pad maar een protocol-relatieve URL. De browser vult het schema van de huidige pagina aan en komt uit ophttps://evil.example, terwijl de waarde netjes met een slash begint./\evil.exampleen\/evil.exampledoen hetzelfde, omdat browsers een backslash in dit deel van het adres als slash behandelen.https://app.example.com@evil.example/gaat naarevil.example. Alles voor de apenstaart is gebruikersinformatie en geen host, maar voor een mens leest de link als uw domein.https://app.example.com.evil.example/passeert elke controle die alleen kijkt of uw domeinnaam ergens in de waarde voorkomt.
Ernstiger wordt het zodra de doorverwijzing in een OAuth-stroom zit. Een authorization server accepteert alleen vooraf geregistreerde redirect_uri-waarden, juist om te voorkomen dat een token bij een vreemde belandt. Zit op zo’n geregistreerd adres een open redirect, dan is die registratie waardeloos: de authorization code of het token gaat keurig naar uw domein en wordt daarna door uw eigen doorverwijzing aan de aanvaller doorgegeven.
De veilige variant laat de gebruiker geen adres kiezen, maar een sleutel die de applicatie serverzijdig naar een vast pad vertaalt.
Veilig:
// Vaste bestemmingen; de gebruiker stuurt alleen de sleutel mee
const RETURN_TARGETS = new Map([
["dashboard", "/dashboard"],
["facturen", "/account/facturen"],
["rapporten", "/rapporten/overzicht"]
]);
app.post("/login", async (req, res) => {
const user = await authenticate(req.body.username, req.body.password);
if (!user) return res.status(401).render("login");
req.session.userId = user.id;
const key = String(req.query.next ?? "");
res.redirect(RETURN_TARGETS.get(key) ?? "/dashboard");
});
Is een vaste lijst te beperkend, accepteer dan uitsluitend relatieve paden en toets die met een echte URL-parser tegen uw eigen origin:
const BASE = "https://app.example.com";
function veiligPad(waarde) {
// Precies een leidende slash, geen tweede slash en geen backslash erachter
if (typeof waarde !== "string" || !/^\/[^/\\]/.test(waarde)) return "/dashboard";
const doel = new URL(waarde, BASE);
return doel.origin === BASE ? doel.pathname + doel.search : "/dashboard";
}
Wat is de impact van een open redirect?
Op zichzelf blijft de impact beperkt. Er lekken geen gegevens, er verandert niets in uw database en de aanvaller krijgt geen toegang tot uw systemen. Wat hij wint, is geloofwaardigheid: een phishinglink die begint met het domein van uw bank of uw portaal wordt vaker aangeklikt en komt vaker door een mailfilter dan een link naar een onbekend adres. In die vorm is de classificatie laag.
De ernst schuift naar middel zodra de doorverwijzing in een keten zit. In een OAuth-stroom kan zij een authorization code of een access token naar buiten dragen en zo tot volledige accountovername leiden. Hetzelfde geldt voor eenmalige links uit een wachtwoordherstelmail en voor tokens die in de Referer-header meeliften naar de volgende host. Wijst de doorverwijzing niet naar een externe host maar naar javascript: of data:, en wordt zij clientzijdig uitgevoerd, dan levert dat bovendien cross-site scripting op. Zakelijk vertaalt dat zich naar reputatieschade en in het slechtste geval naar plaatsing van uw domein op een blokkeerlijst van een browser of mailprovider.
Hoe spoort u een open redirect op?
Begin met een inventarisatie van elke plek waar de applicatie een bestemming uit invoer haalt. Zoek in de code naar redirect, naar het zetten van de Location-header en, aan de clientkant, naar toekenningen aan location.href, naar location.replace en naar meta-refresh-tags. Zoek daarnaast in verkeer en logboeken naar parameternamen als returnUrl, next, url en dest, ook in POST-bodies.
Testen doet u door de waarde te vervangen door een domein dat u zelf beheert en de respons op te vragen zonder de doorverwijzing te volgen, bijvoorbeeld met curl -i, zodat u de Location-header rechtstreeks ziet. Wordt die waarde geweigerd, loop dan de bypasses hierboven af: de dubbele slash, de backslash-varianten, de apenstaart en uw domeinnaam als subdomein. Vergeet de clientzijdige gevallen niet, waar de doorverwijzing in JavaScript wordt uitgevoerd.
Scanners melden het leerboekgeval, maar leveren veel ruis op en missen de doorverwijzingen achter een inlogscherm of halverwege een proces met meerdere stappen. AssistSec neemt open redirects mee in een penetratietest en toetst daarbij ook de keten: of de doorverwijzing bruikbaar is om een token of een eenmalige link naar buiten te trekken.
Hoe voorkomt u een open redirect?
- Stuur sleutels mee, geen adressen. Laat de gebruiker een korte identificatie meegeven die u serverzijdig naar een vast pad vertaalt. Wat niet in uw lijst staat, gaat naar de standaardbestemming.
- Accepteer uitsluitend relatieve paden. Weiger elke waarde met een schema, een host of een apenstaart, en normaliseer het resultaat voordat u het in de
Location-header zet. - Wijs een dubbele slash en elke backslash expliciet af. Dit zijn de twee bypasses die een controle op het eerste teken in vrijwel elke browser overleven.
- Valideer met een URL-parser, nooit met een substring-controle. Vergelijk de volledige origin, dus schema, host en poort samen.
- Behandel
redirect_uri, herstel-links en SSO-terugkeerpunten apart. Vergelijk ze exact met een geregistreerde waarde en sta geen jokertekens of vrije padsuffixen toe. - Toon een tussenscherm als een externe doorverwijzing echt nodig is. Laat de bestemming zien en laat de bezoeker bevestigen.
Bronnen
- CWE-601: URL Redirection to Untrusted Site (Open Redirect)cwe.mitre.org
- OWASP Cheat Sheet: Unvalidated Redirects and Forwardscheatsheetseries.owasp.org
- PortSwigger Web Security Academy: OAuth 2.0 authentication vulnerabilitiesportswigger.net
- RFC 9700: Best Current Practice for OAuth 2.0 Securitydatatracker.ietf.org
Veelgestelde vragen
Is een open redirect echt een kwetsbaarheid?
Ja, al is de ernst op zichzelf beperkt. Er lekken geen gegevens en er verandert niets aan uw data; de aanvaller leent alleen de reputatie van uw domein. Zodra de doorverwijzing onderdeel wordt van een keten, bijvoorbeeld in een OAuth-stroom of bij een wachtwoordherstel-link, stijgt de ernst wel degelijk. Behandel een open redirect daarom als een reëel defect en niet als cosmetisch commentaar.
Waarom werkt //evil.example terwijl mijn controle op een leidende slash test?
Omdat twee slashes aan het begin geen pad zijn maar een protocol-relatieve URL. De browser vult het schema van de huidige pagina aan en komt uit op https://evil.example. De waarde begint netjes met een slash, dus een controle op het eerste teken laat hem door. Dezelfde truc werkt met een backslash, omdat browsers die in dit deel van de URL als slash behandelen.
Wat heeft een open redirect met OAuth te maken?
Een authorization server accepteert alleen redirect_uri-waarden die vooraf zijn geregistreerd. Staat op zo'n geregistreerd adres een open redirect, dan is die registratie geen bescherming meer: de authorization code of het token wordt eerst naar uw domein gestuurd en daarna door uw eigen doorverwijzing aan de aanvaller doorgegeven. Dit is de reden dat de OAuth-richtlijnen expliciet eisen dat clients geen open redirects bevatten.
Hoe houd ik de returnUrl-functionaliteit zonder de kwetsbaarheid?
U hoeft de functie niet te schrappen, alleen de vrijheid van de invoer te beperken. Laat de gebruiker een sleutel meesturen die u serverzijdig naar een vast pad vertaalt, of accepteer uitsluitend relatieve paden en toets die met een echte URL-parser tegen uw eigen origin. Beide varianten houden het terugkeergedrag intact en sluiten externe bestemmingen structureel uit.
Vangt een Content Security Policy een open redirect af?
Nee. Een Content Security Policy beperkt welke bronnen een pagina mag laden en uitvoeren, maar een open redirect is een gewone HTTP-respons met status 302 en een Location-header. De browser volgt die doorverwijzing voordat er een pagina met een policy in beeld komt. Alleen validatie van de bestemming aan serverzijde helpt.
Verwante artikelen
- BegrippenPentestEen pentest is een gecontroleerde aanval op uw systemen door ethische hackers. Lees hoe een penetratietest werkt en welke kwetsbaarheden u ermee vindt.
- KwetsbaarhedenCWE-284A01:2021Broken access controlBroken access control uitgelegd: horizontale en verticale escalatie, forced browsing, en hoe u met deny by default serverzijdig autoriseert.
- KwetsbaarhedenCWE-79A03:2021Cross-site scripting (XSS)Cross-site scripting (XSS) laat aanvallers scripts injecteren die in de browser van uw bezoekers draaien. Zo werkt de aanval en zo voorkomt u het.
- KwetsbaarhedenCWE-644A03:2021Host header injectionBouwt uw applicatie links op met de Host-header, dan bepaalt de aanvaller waar die naartoe wijzen. Lees hoe dat wachtwoordherstel kaapt.
- KwetsbaarhedenCWE-918A10:2021Server-side request forgery (SSRF)Server-side request forgery (SSRF) uitgelegd: hoe aanvallers uw server dwingen interne systemen en clouddiensten te bereiken, en hoe u het voorkomt.