mirror of
https://github.com/github/codeql.git
synced 2026-04-30 03:05:15 +02:00
Merge pull request #8054 from asgerf/js/split-request-forgery
JS: split request forgery query into server-side and client-side variants
This commit is contained in:
@@ -0,0 +1,64 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
<overview>
|
||||
<p>
|
||||
|
||||
Directly incorporating user input in the URL of an outgoing HTTP request
|
||||
can enable a request forgery attack, in which the request is altered to
|
||||
target an unintended API endpoint or resource.
|
||||
|
||||
A client-side forged request may perform an unwanted action affecting the victim's account,
|
||||
or may lead to cross-site scripting if the request response is handled in an unsafe way.
|
||||
|
||||
This is different from CSRF (cross-site request forgery), and will usually bypass CSRF protections.
|
||||
|
||||
This is usually less severe than SSRF (server-side request forgery), as it does not expose internal services.
|
||||
</p>
|
||||
</overview>
|
||||
|
||||
<include src="RequestForgeryRecommendation.inc.qhelp"/>
|
||||
|
||||
<example>
|
||||
|
||||
<p>
|
||||
|
||||
The following example shows an HTTP request used to fetch the pre-rendered
|
||||
HTML body of a message. It is using the endpoint <code>/api/messages/ID</code>, which
|
||||
is believed to respond with a safe HTML string, to be embedded in the page:
|
||||
|
||||
</p>
|
||||
|
||||
<sample src="examples/ClientSideRequestForgeryBad.js"/>
|
||||
|
||||
<p>
|
||||
|
||||
However, the format of the message ID is not checked, and an attacker can abuse this to
|
||||
alter the endpoint targeted by the request. If they can redirect it to an endpoint that returns
|
||||
an untrusted value, this leads to cross-site scripting.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
For example, given the query string <code>message_id=../pastebin/123</code>, the request will
|
||||
end up targeting the <code>/api/pastebin</code> endpoint. Or if there is an open redirect on the login page,
|
||||
a query string like <code>message_id=../../login?redirect_url=https://evil.com</code> could give
|
||||
the attacker full control over the response as well.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
In example below, the input has been restricted to a number so the endpoint cannot be altered:
|
||||
</p>
|
||||
|
||||
<sample src="examples/ClientSideRequestForgeryGood.js"/>
|
||||
|
||||
</example>
|
||||
|
||||
<references>
|
||||
|
||||
<li>OWASP: <a href="https://cwe.mitre.org/data/definitions/918.html">Server-side request forgery</a></li>
|
||||
<li>OWASP: <a href="https://cwe.mitre.org/data/definitions/352.html">Cross-site request forgery</a></li>
|
||||
|
||||
</references>
|
||||
</qhelp>
|
||||
@@ -0,0 +1,23 @@
|
||||
/**
|
||||
* @name Client-side request forgery
|
||||
* @description Making a client-to-server request with user-controlled data in the URL allows a request forgery attack
|
||||
* against the client.
|
||||
* @kind path-problem
|
||||
* @problem.severity error
|
||||
* @security-severity 5.0
|
||||
* @precision medium
|
||||
* @id js/client-side-request-forgery
|
||||
* @tags security
|
||||
* external/cwe/cwe-918
|
||||
*/
|
||||
|
||||
import javascript
|
||||
import semmle.javascript.security.dataflow.ClientSideRequestForgeryQuery
|
||||
import DataFlow::PathGraph
|
||||
|
||||
from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink, DataFlow::Node request
|
||||
where
|
||||
cfg.hasFlowPath(source, sink) and
|
||||
request = sink.getNode().(Sink).getARequest()
|
||||
select request, source, sink, "The $@ of this request depends on $@.", sink.getNode(),
|
||||
sink.getNode().(Sink).getKind(), source, "a user-provided value"
|
||||
@@ -6,40 +6,28 @@
|
||||
<overview>
|
||||
<p>
|
||||
|
||||
Directly incorporating user input into an HTTP request
|
||||
without validating the input can facilitate different kinds of request
|
||||
forgery attacks, where the attacker essentially controls the request.
|
||||
Directly incorporating user input in the URL of an outgoing HTTP request
|
||||
can enable a request forgery attack, in which the request is altered to
|
||||
target an unintended API endpoint or resource.
|
||||
|
||||
If the vulnerable request is in server-side code, then security
|
||||
mechanisms, such as external firewalls, can be bypassed.
|
||||
If the server performing the request is connected to an internal network, this can give an attacker
|
||||
the means to bypass the network boundary and make requests against internal services.
|
||||
|
||||
If the vulnerable request is in client-side code, then unsuspecting
|
||||
users can send malicious requests to other servers, potentially
|
||||
resulting in a DDOS attack.
|
||||
A forged request may perform an unintended action on behalf of the attacker, or cause information
|
||||
leak if redirected to an external server or if the request response is fed back to the user.
|
||||
It may also compromise the server making the request, if the request response is handled in an unsafe way.
|
||||
|
||||
</p>
|
||||
</overview>
|
||||
|
||||
<recommendation>
|
||||
|
||||
<p>
|
||||
|
||||
To guard against request forgery, it is advisable to avoid
|
||||
putting user input directly into a network request. If a flexible
|
||||
network request mechanism is required, it is recommended to maintain a
|
||||
list of authorized request targets and choose from that list based on
|
||||
the user input provided.
|
||||
|
||||
</p>
|
||||
|
||||
</recommendation>
|
||||
<include src="RequestForgeryRecommendation.inc.qhelp"/>
|
||||
|
||||
<example>
|
||||
|
||||
<p>
|
||||
|
||||
The following example shows an HTTP request parameter
|
||||
being used directly in a URL request without validating the input,
|
||||
being used directly in the URL of a request without validating the input,
|
||||
which facilitates an SSRF attack. The request
|
||||
<code>http.get(...)</code> is vulnerable since attackers can choose
|
||||
the value of <code>target</code> to be anything they want. For
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
/**
|
||||
* @name Uncontrolled data used in network request
|
||||
* @description Sending network requests with user-controlled data allows for request forgery attacks.
|
||||
* @name Server-side request forgery
|
||||
* @description Making a network request with user-controlled data in the URL allows for request forgery attacks.
|
||||
* @kind path-problem
|
||||
* @problem.severity error
|
||||
* @security-severity 9.1
|
||||
* @precision medium
|
||||
* @precision high
|
||||
* @id js/request-forgery
|
||||
* @tags security
|
||||
* external/cwe/cwe-918
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
<recommendation>
|
||||
|
||||
<p>
|
||||
Restrict user inputs in the URL of an outgoing request, in particular:
|
||||
</p>
|
||||
<ul>
|
||||
<li>
|
||||
Avoid user input in the hostname of the URL.
|
||||
Pick the hostname from an allow-list instead of constructing it directly from user input.
|
||||
</li>
|
||||
<li>
|
||||
Take care when user input is part of the pathname of the URL.
|
||||
Restrict the input so that path traversal ("<code>../</code>")
|
||||
cannot be used to redirect the request to an unintended endpoint.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
</recommendation>
|
||||
|
||||
</qhelp>
|
||||
@@ -0,0 +1,6 @@
|
||||
async function loadMessage() {
|
||||
const query = new URLSearchParams(location.search);
|
||||
const url = '/api/messages/' + query.get('message_id');
|
||||
const data = await (await fetch(url)).json();
|
||||
document.getElementById('message').innerHTML = data.html;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
async function loadMessage() {
|
||||
const query = new URLSearchParams(location.search);
|
||||
const url = '/api/messages/' + Number(query.get('message_id'));
|
||||
const data = await (await fetch(url)).json();
|
||||
document.getElementById('message').innerHTML = data.html;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
---
|
||||
category: queryMetadata
|
||||
---
|
||||
* The `js/request-forgery` query previously flagged both server-side and client-side request forgery,
|
||||
but these are now handled by two different queries:
|
||||
* `js/request-forgery` is now specific to server-side request forgery. Its precision has been raised to
|
||||
`high` and is now shown by default (it was previously in the `security-extended` suite).
|
||||
* `js/client-side-request-forgery` is specific to client-side request forgery. This is technically a new query
|
||||
but simply flags a subset of what the old query did.
|
||||
This has precision `medium` and is part of the `security-extended` suite.
|
||||
Reference in New Issue
Block a user