URL manipulation cheatsheet for JavaScript.
Please DO NOT concat url and user input without escape
// DO NOT
const name = "<user input>";
const url = `https://example.com/user/${name}`;
console.log(url); // => "https://example.com/user/<user input>"
This code may have directory traversal vulnerability.
You should escape the name
by encodeURIComponent
.
// DO
const name = "<user input>";
const url = `https://example.com/user/${encodeURIComponent(name)}`;
console.log(url); // => "https://example.com/user/%3Cuser%20input%3E"
Addtionaly, You should reject .
and ..
as a name.
Because encodeURIComponent("..")
is ..
, it may have directory traversal vulnerability.
// DO
const name = "<user input>";
if (name === ".." || name === ".") {
throw new Error("Invalid name");
}
const url = `https://example.com/user/${encodeURIComponent(name)}`;
console.log(url); // => "https://example.com/user/%3Cuser%20input%3E"
- https://url.spec.whatwg.org/#example-url-parsing
- Path Traversal | OWASP Foundation
- Path Traversal and SSRF - Security, Tech, And Ramblings
Please DO NOT concat parameter and user input without escape
// DO NOT
const query = "<user input>";
const url = `https://example.com?q=${query}`;
console.log(url); // => "https://example.com?q=<user input>"
This example does not consider that query
includes &
or ?
that is required to escape.
You should escape the query
by encodeURIComponent
.
// DO
const query = "<user input>";
const url = `https://example.com?q=${encodeURIComponent(query)}`;
console.log(url); // => "https://example.com?q=%3Cuser%20input%3E"
Or, You can use URLSearchParams() that escape each parameters automatically.
- keywords: url-join, join path
const base = "https://example.com";
const pathname = "/path/to/page";
const result = new URL(pathname, base);
console.log(result.toString()); // => "https://example.com/path/to/page"
If the pathname include user input, you should escape it by encodeURIComponent
.
const base = "https://example.com/";
const name = "<user input>";
const result = new URL(`/user/${encodeURIComponent(name)}`, base);
console.log(result.toString()); // => "https://example.com/user/%3Cuser%20input%3E"
Addtionaly, You should reject .
and ..
as a name.
Because encodeURIComponent("..")
is ..
, it may have directory traversal vulnerability.
// DO
const base = "https://example.com/";
const name = "<user input>";
if (name === ".." || name === ".") {
throw new Error("Invalid name");
}
const result = new URL(`/user/${encodeURIComponent(name)}`, base);
console.log(result.toString()); // => "https://example.com/user/%3Cuser%20input%3E"
- https://url.spec.whatwg.org/#example-url-parsing
- Path Traversal | OWASP Foundation
- Path Traversal and SSRF - Security, Tech, And Ramblings
Use URL
and URLSearchParams#get
const inputURL = "https://example.com/?q=query&page=1";
const url = new URL(inputURL);
const q = url.searchParams.get("q");
console.log(q); // => "query"
Use URL
and URLSearchParams#getAll
const inputURL = "https://example.com/?q=query&lang=en_US&lang=ja_JP";
const url = new URL(inputURL);
const langs = url.searchParams.getAll("lang");
console.log(langs); // ["en_US", "ja_JP"]
Use URLSearchParams
const q = "query";
const page = 1;
const base = "https://example.com";
const url = new URL(base);
const params = new URLSearchParams({
q,
page,
});
console.log(url + "?" + params); // => "https://example.com/?q=query&page=1"
or
const q = "query";
const page = 1;
const base = "https://example.com";
const url = new URL(base);
url.search = new URLSearchParams({
q,
page,
});
console.log(url.toString()); // => "https://example.com/?q=query&page=1"
đź“ť URLSearchParams
escape each parameter automtically.
const q = "<user input>";
const page = 1;
const base = "https://example.com";
const url = new URL(base);
url.search = new URLSearchParams({
q,
page,
});
console.log(url.toString()); // => "https://example.com/?q=%3Cuser+input%3E&page=1"
Use URL
's searchParams
property.
const inputURL = "https://example.com/?q=query&page=1";
const url = new URL(inputURL);
url.searchParams.set("q", "update");
console.log(url.toString()); // => "https://example.com/?q=update&page=1"
Use URL
and URLSearchParams
const inputURL = "https://example.com/?q=query&page=1";
const url = new URL(inputURL);
url.searchParams.delete("q");
console.log(url.toString()); // => "https://example.com/?page=1"
Allow only a
and d
parameters.
- keywords: pick, white list, allow list
const base = "https://example.com/?a=1&b=2&c=3&d=4";
const url = new URL(base);
const allowedParameterNames = ["a", "d"];
url.search = new URLSearchParams(
Array.from(url.searchParams).filter(([key, value]) => {
return allowedParameterNames.includes(key);
})
);
console.log(url.toString()); // => "https://example.com/?a=1&d=4"
new URL(urlString)
throw an error when parsing relative url string.
As a result, you can use URL
for checking URL is absolute-URL that starts with a schema like https:
const isValidURL = (urlString) => {
try {
new URL(urlString); // if `urlString` is invalid, throw an erorr
return true;
} catch {
return false;
}
};
console.log(isValidURL("https://example.com")); // => true
console.log(isValidURL("https/example.com")); // => false
Check URL
's protocol
property.
const isHttpURL = (urlString) => {
try {
const url = new URL(urlString); // if `urlString` is invalid, throw an erorr
return url.protocol === "http:" || url.protocol === "https:";
} catch {
return false;
}
};
console.log(isHttpURL("http://example.com")); // => true
console.log(isHttpURL("https://example.com")); // => true
console.log(isHttpURL("ftp://example.com")); // => false
console.log(isHttpURL("https/example.com")); // => false