Skip to content

Commit 8ac0ec8

Browse files
committed
JS: Write help for ClientSideRequestForgery
1 parent 91c6415 commit 8ac0ec8

File tree

3 files changed

+93
-0
lines changed

3 files changed

+93
-0
lines changed
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
<!DOCTYPE qhelp PUBLIC
2+
"-//Semmle//qhelp//EN"
3+
"qhelp.dtd">
4+
<qhelp>
5+
6+
<overview>
7+
<p>
8+
9+
Directly incorporating user input in the URL of an outgoing HTTP request
10+
can enable a request forgery attack, in which the request is altered to
11+
target an unintended API endpoint or resource.
12+
13+
A client-side forged request may perform an unwanted action affecting the victim's account,
14+
or may lead to cross-site scripting if the request response is handled in an unsafe way.
15+
16+
This is different from CSRF (cross-site request forgery), and will usually bypass CSRF protections.
17+
18+
This is usually less severe than SSRF (server-side request forgery), as it does not expose internal services.
19+
</p>
20+
</overview>
21+
22+
<recommendation>
23+
24+
<p>
25+
Restrict user inputs in the URL of an outgoing request, in particular:
26+
<ul>
27+
<li>
28+
Avoid user input in the hostname of the URL.
29+
Pick the hostname from an allow-list instead of constructing it directly from user input.
30+
</li>
31+
<li>
32+
Take care when user input is part of the pathname of the URL.
33+
Restrict the input so that path traversal ("<code>../<code>")
34+
cannot be used to redirect the request to an unintended endpoint.
35+
</li>
36+
</ul>
37+
</p>
38+
39+
</recommendation>
40+
41+
<example>
42+
43+
<p>
44+
45+
The following example shows an HTTP request used to fetch the pre-rendered
46+
HTML body of a message. It is using the endpoint <code>/api/messages/ID</code>, which
47+
is believed to respond with a safe HTML string, to be embedded in the page:
48+
49+
</p>
50+
51+
<sample src="examples/ClientSideRequestForgeryBad.js"/>
52+
53+
<p>
54+
55+
However, the format of the message ID is not checked, and an attacker can abuse this to
56+
alter the endpoint targeted by the request. If they can redirect it to an endpoint that returns
57+
an untrusted value, this leads to cross-site scripting.
58+
</p>
59+
60+
<p>
61+
For example, given the query string <code>message_id=../pastebin/123</code>, the request will
62+
end up targeting the <code>/api/pastebin</code> endpoint. Or if there is an open redirect on the login page,
63+
a query string like <code>message_id=../../login?redirect_url=https://evil.com</code> could give
64+
the attacker full control over the response as well.
65+
</p>
66+
67+
<p>
68+
In example below, the input has been restricted to a number so the endpoint cannot be altered:
69+
</p>
70+
71+
<sample src="examples/ClientSideRequestForgeryGood.js"/>
72+
73+
</example>
74+
75+
<references>
76+
77+
<li>OWASP: <a href="https://cwe.mitre.org/data/definitions/918.html">Server-side request forgery</a></li>
78+
<li>OWASP: <a href="https://cwe.mitre.org/data/definitions/352.html">Cross-site request forgery</a></li>
79+
80+
</references>
81+
</qhelp>
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
async function loadMessage() {
2+
const query = new URLSearchParams(location.search);
3+
const url = '/api/messages/' + query.get('message_id');
4+
const data = await (await fetch(url)).json();
5+
document.getElementById('message').innerHTML = data.html;
6+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
async function loadMessage() {
2+
const query = new URLSearchParams(location.search);
3+
const url = '/api/messages/' + Number(query.get('message_id'));
4+
const data = await (await fetch(url)).json();
5+
document.getElementById('message').innerHTML = data.html;
6+
}

0 commit comments

Comments
 (0)