new URL() & allowed schemes
Dangerous URLs
Lesson 3 of 20 · 10 min · Module 1: Untrusted input
Links and images take an address, and addresses can carry code: a link to javascript:… runs script when someone clicks it. Any address that comes from a person needs the same care as their text.
Start this lesson →Free. No installation: you write and run the code in your browser, and it is checked automatically.
By the end, you’ll be able to…
- Explain how addresses can carry code
- Parse addresses with new URL() before trusting them
- Allow only the schemes you need
The idea
new URL(value) parses an address the way the browser will, so you can check its protocol. Allow only https: and http:, and refuse everything else, including javascript:, data: and values that are not addresses at all. Attackers hide the scheme with capital letters, spaces or tabs, so never check with simple string comparisons.
const url = new URL(' JaVaScRiPt:alert(1)');
console.log(url.protocol); // "javascript:"
01Some addresses are programs
A javascript: address runs its code when the link is followed, and a data: address can contain a whole page. Both are dangerous wherever people choose a link, an image or a place to redirect to.
02Parse like the browser
Browsers ignore capital letters in the scheme, skip spaces at the start, and remove tabs and line breaks anywhere, so ' JaVa\tScript:' still counts as javascript:. new URL() applies exactly those rules, which is why its protocol is the value to check.
console.log(new URL(' JAVA\tSCRIPT:alert(1)').protocol);javascript:03Allow the scheme, don’t deny it
Checking only for javascript: is a deny-list again: vbscript:, data: and schemes nobody thought of would slip through. Accept https: and http:, and return null for everything else.
Your turn
safeUrl(value). It returns the parsed address (url.href) when the protocol is https: or http:, and null for every other scheme, for values that cannot be parsed, and for values that are not text. The page already uses it to decide which members get a website link.What the checks look for
- Web addresses are accepted
- javascript: is refused in every disguise
- Other schemes and broken addresses are refused
- Addresses come back the way the browser reads them
- The page links only to safe addresses
Take this with you
Parse every address, allow only http: and https:, and refuse the rest.
Go a little further
Show the website’s hostname as the link text, and add a small note when a member links to http: instead of https:.