From 3760d8ed4008eb0b793a31d359ccb025998c1ad0 Mon Sep 17 00:00:00 2001 From: azu Date: Mon, 21 Aug 2023 23:38:04 +0900 Subject: [PATCH] Add `.` and `..` checking fix https://github.com/azu/url-cheatsheet/issues/11 --- README.md | 70 ++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 54 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 29f7a5c..154e204 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ Please DO NOT concat url and user input without escape ```js // DO NOT -const name = "" +const name = ""; const url = `https://example.com/user/${name}`; console.log(url); // => "https://example.com/user/" ``` @@ -18,18 +18,35 @@ You should escape the `name` by `encodeURIComponent`. ```js // DO -const name = "" +const name = ""; const url = `https://example.com/user/${encodeURIComponent(name)}`; console.log(url); // => "https://example.com/user/%3Cuser%20input%3E" ``` +Addtionaly, You should reject [`.`](https://url.spec.whatwg.org/#single-dot-path-segment) and [`..`](https://url.spec.whatwg.org/#double-dot-path-segment) as a name. +Because `encodeURIComponent("..")` is `..`, it may have directory traversal vulnerability. + +```js +// DO +const name = ""; +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" +``` + +- +- [Path Traversal | OWASP Foundation](https://owasp.org/www-community/attacks/Path_Traversal) +- [Path Traversal and SSRF - Security, Tech, And Ramblings](https://smarpo.com/posts/path-traversal-and-ssrf/) + ## DO NOT: concat parameter and user input without escape Please DO NOT concat parameter and user input without escape ```js // DO NOT -const query = "" +const query = ""; const url = `https://example.com?q=${query}`; console.log(url); // => "https://example.com?q=" ``` @@ -39,7 +56,7 @@ You should escape the `query` by `encodeURIComponent`. ```js // DO -const query = "" +const query = ""; const url = `https://example.com?q=${encodeURIComponent(query)}`; console.log(url); // => "https://example.com?q=%3Cuser%20input%3E" ``` @@ -48,7 +65,7 @@ Or, You can use [URLSearchParams()](https://developer.mozilla.org/en-US/docs/Web - Related: [Client-side HTTP parameter pollution (reflected) - PortSwigger](https://portswigger.net/kb/issues/00501400_client-side-http-parameter-pollution-reflected) -## Base URL + Path +## Base URL + Path Use [`new URL(pathname, base)`](https://developer.mozilla.org/docs/Web/API/URL/URL). @@ -65,11 +82,30 @@ If the pathname include user input, you should escape it by `encodeURIComponent` ```js const base = "https://example.com/"; -const name = "" +const name = ""; +const result = new URL(`/user/${encodeURIComponent(name)}`, base); +console.log(result.toString()); // => "https://example.com/user/%3Cuser%20input%3E" +``` + +Addtionaly, You should reject [`.`](https://url.spec.whatwg.org/#single-dot-path-segment) and [`..`](https://url.spec.whatwg.org/#double-dot-path-segment) as a name. +Because `encodeURIComponent("..")` is `..`, it may have directory traversal vulnerability. + +```js +// DO +const base = "https://example.com/"; +const name = ""; +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" ``` +- +- [Path Traversal | OWASP Foundation](https://owasp.org/www-community/attacks/Path_Traversal) +- [Path Traversal and SSRF - Security, Tech, And Ramblings](https://smarpo.com/posts/path-traversal-and-ssrf/) + + ## Get parameter from URL Use [`URL`](https://developer.mozilla.org/docs/Web/API/URL/URL) and [URLSearchParams#get](https://developer.mozilla.org/docs/Web/API/URLSearchParams/get) @@ -102,13 +138,13 @@ const page = 1; const base = "https://example.com"; const url = new URL(base); const params = new URLSearchParams({ - q, - page + q, + page, }); console.log(url + "?" + params); // => "https://example.com/?q=query&page=1" ``` -or +or ```js const q = "query"; @@ -116,8 +152,8 @@ const page = 1; const base = "https://example.com"; const url = new URL(base); url.search = new URLSearchParams({ - q, - page + q, + page, }); console.log(url.toString()); // => "https://example.com/?q=query&page=1" ``` @@ -130,8 +166,8 @@ const page = 1; const base = "https://example.com"; const url = new URL(base); url.search = new URLSearchParams({ - q, - page + q, + page, }); console.log(url.toString()); // => "https://example.com/?q=%3Cuser+input%3E&page=1" ``` @@ -168,9 +204,11 @@ Allow only `a` and `d` parameters. 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); -})); +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" ```