Open Redirect
How a trusted-looking link can bounce a victim to an attacker's site, and why checking the URL as text never holds.
- Track
- Web security
- Estimated time
- 20 min
- Level
- Intermediate
Before this lesson
An open redirect is a page that sends the browser to a URL you supply, with no check that the destination belongs to the site. On its own it does not steal data from the server. Its danger is trust: the link starts on a domain the victim knows, so they click it, and it quietly lands them somewhere else — a phishing page, or a URL that captures a token passed along in the redirect.
What is the vulnerability?
A login page wants to send you back where you came from after you sign in, so it takes a next parameter and redirects to it:
app.post('/login', (req, res) => {
// Redirects to whatever the user put in next.
res.redirect(req.body.next);
});A link like https://login.ravens.lab/login?next=https://falcon.owl.lab/phish begins on the trusted domain but ends on the attacker’s. The victim reads only the first part.
Why does it happen?
The server treats a user-supplied value as a safe destination. Defenders then try to “fix” it by checking the URL as text — does it start with /, does it contain our domain — instead of parsing it. A URL has structure the text does not reveal: //host is absolute, and https://trusted@attacker/ has its real host after the @. Every text check has a spelling it misses.
How do you stop it?
Do not accept a full URL from the user. Prefer an id mapped to a known internal path, or a pure relative path. When you must compare a destination, parse it and compare the resulting host against an allowlist by exact match — never a substring of the raw string.
const ALLOWED = new Set(['login.ravens.lab', 'www.ravens.lab']);
app.post('/login', (req, res) => {
const url = new URL(req.body.next, 'https://login.ravens.lab');
res.redirect(ALLOWED.has(url.host) ? url.href : '/dashboard');
});The three labs below walk this ladder: no check, a leading-slash check bypassed by //host, and a substring allowlist bypassed by the @ userinfo trick.
Labs
A redirect to anywhere
SolvedEasyBrowser lab
Make the redirect land on an external site you control, falcon.owl.lab.
Enter the labLockedRelative URLs only
SolvedMediumBrowser lab
Make the redirect land on the external site falcon.owl.lab despite the leading-slash check.
Enter the labLockedA substring allowlist
SolvedHardBrowser lab
Make the redirect land on falcon.owl.lab even though the URL contains the trusted domain.
Enter the labLocked
