Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit f5a6af5

Browse files
author
Esben Sparre Andreasen
committed
JS: add security query: js/request-forgery
1 parent 2104cf5 commit f5a6af5

9 files changed

Lines changed: 254 additions & 0 deletions

File tree

javascript/config/suites/javascript/security

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,4 @@
2929
+ semmlecode-javascript-queries/Security/CWE-807/DifferentKindsComparisonBypass.ql: /Security/CWE/CWE-807
3030
+ semmlecode-javascript-queries/Security/CWE-843/TypeConfusionThroughParameterTampering.ql: /Security/CWE/CWE-834
3131
+ semmlecode-javascript-queries/Security/CWE-916/InsufficientPasswordHash.ql: /Security/CWE/CWE-916
32+
+ semmlecode-javascript-queries/Security/CWE-918/RequestForgery.ql: /Security/CWE/CWE-918
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
<!DOCTYPE qhelp PUBLIC
2+
"-//Semmle//qhelp//EN"
3+
"qhelp.dtd">
4+
<qhelp>
5+
6+
<overview>
7+
<p>
8+
9+
Directly incorporating user input into a remote request
10+
without validating the input can facilitate different kinds of request
11+
forgery attacks, where the attacker essentially controls the request.
12+
13+
If the vulnerable request is in server-side code, then security
14+
mechanisms, such as external firewalls, can be bypassed.
15+
16+
If the vulnerable request is in client-side code, then unsuspecting
17+
users can send malicious requests to other servers, potentially
18+
resulting in a DDOS attack.
19+
20+
</p>
21+
</overview>
22+
23+
<recommendation>
24+
25+
<p>
26+
27+
To guard against request forgery, it is advisable to avoid
28+
putting user input directly into a remote request. If a flexible
29+
remote request mechanism is required, it is recommended to maintain a
30+
list of authorized request targets and choose from that list based on
31+
the user input provided.
32+
33+
</p>
34+
35+
</recommendation>
36+
37+
<example>
38+
39+
<p>
40+
41+
The following example shows an HTTP request parameter
42+
being used directly in a URL request without validating the input,
43+
which facilitate an SSRF attack. The request
44+
<code>http.get(...)</code> is vulnerable since an attacker can choose
45+
the value of <code>target</code> to be anything he wants. For
46+
instance, the attacker can choose
47+
<code>"internal.example.com/#"</code> as the target, causing the URL
48+
used in the request to be
49+
<code>"https://internal.example.com/#.example.com/data"</code>.
50+
51+
</p>
52+
53+
<p>
54+
55+
A request to <code>https://internal.example.com</code> may
56+
be problematic if that server is not meant to be
57+
directly accessible from the attacker's machine.
58+
59+
</p>
60+
61+
<sample src="examples/RequestForgeryBad.js"/>
62+
63+
<p>
64+
65+
One way to remedy the problem is to use the user input to
66+
select a known fixed string before performing the request:
67+
68+
</p>
69+
70+
<sample src="examples/RequestForgeryGood.js"/>
71+
72+
</example>
73+
74+
<references>
75+
76+
<li>OWASP: <a href="https://www.owasp.org/index.php/Server_Side_Request_Forgery">SSRF</a></li>
77+
78+
</references>
79+
</qhelp>
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/**
2+
* @name Uncontrolled data used in remote request
3+
* @description Sending remote requests with user-controlled data allows for request forgery attacks.
4+
* @kind problem
5+
* @problem.severity error
6+
* @precision high
7+
* @id js/request-forgery
8+
* @tags security
9+
* external/cwe/cwe-918
10+
*/
11+
12+
import javascript
13+
import semmle.javascript.security.dataflow.RequestForgery::RequestForgery
14+
15+
from Configuration cfg, DataFlow::Node source, Sink sink, DataFlow::Node request
16+
where cfg.hasFlow(source, sink) and
17+
request = sink.getARequest()
18+
select request, "The $@ of this request depends on $@.", sink, sink.getKind(), source, "a user-provided value"
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import http from 'http';
2+
import url from 'url';
3+
4+
var server = http.createServer(function(req, res) {
5+
var target = url.parse(request.url, true).query.target;
6+
7+
// BAD: `target` is controlled by the attacker
8+
http.get('https://' + target + ".example.com/data/", res => {
9+
// process request response ...
10+
});
11+
12+
});
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import http from 'http';
2+
import url from 'url';
3+
4+
var server = http.createServer(function(req, res) {
5+
var target = url.parse(request.url, true).query.target;
6+
7+
var subdomain;
8+
if (target === 'EU') {
9+
subdomain = "europe"
10+
} else {
11+
subdomain = "world"
12+
}
13+
14+
// GOOD: `subdomain` is controlled by the server
15+
http.get('https://' + subdomain + ".example.com/data/", res => {
16+
// process request response ...
17+
});
18+
19+
});
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
/**
2+
* Provides a taint-tracking configuration for reasoning about request forgery.
3+
*/
4+
5+
import semmle.javascript.security.dataflow.RemoteFlowSources
6+
import UrlConcatenation
7+
8+
module RequestForgery {
9+
10+
/**
11+
* A data flow source for request forgery.
12+
*/
13+
abstract class Source extends DataFlow::Node { }
14+
15+
/**
16+
* A data flow sink for request forgery.
17+
*/
18+
abstract class Sink extends DataFlow::Node {
19+
/**
20+
* Gets a request that uses this sink.
21+
*/
22+
abstract DataFlow::Node getARequest();
23+
24+
/**
25+
* Gets the kind of this sink.
26+
*/
27+
abstract string getKind();
28+
29+
}
30+
31+
/**
32+
* A sanitizer for request forgery.
33+
*/
34+
abstract class Sanitizer extends DataFlow::Node { }
35+
36+
/**
37+
* A taint tracking configuration for request forgery.
38+
*/
39+
class Configuration extends TaintTracking::Configuration {
40+
Configuration() {
41+
this = "RequestForgery"
42+
}
43+
44+
override predicate isSource(DataFlow::Node source) {
45+
source instanceof Source
46+
}
47+
48+
override predicate isSink(DataFlow::Node sink) {
49+
sink instanceof Sink
50+
}
51+
52+
override predicate isSanitizer(DataFlow::Node node) {
53+
super.isSanitizer(node) or
54+
node instanceof Sanitizer
55+
}
56+
57+
override predicate isSanitizer(DataFlow::Node source, DataFlow::Node sink) {
58+
sanitizingPrefixEdge(source, sink)
59+
}
60+
61+
}
62+
63+
/** A source of remote user input, considered as a flow source for request forgery. */
64+
private class RemoteFlowSourceAsSource extends Source {
65+
RemoteFlowSourceAsSource() { this instanceof RemoteFlowSource }
66+
}
67+
68+
/**
69+
* The URL of a URL request, viewed as a sink for request forgery.
70+
*/
71+
private class UrlRequestUrlAsSink extends Sink {
72+
73+
UrlRequest request;
74+
75+
UrlRequestUrlAsSink() {
76+
this = request.getUrl()
77+
}
78+
79+
override DataFlow::Node getARequest() {
80+
result = request
81+
}
82+
83+
override string getKind() {
84+
result = "URL"
85+
}
86+
}
87+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
| tst.js:16:5:16:20 | request(tainted) | The $@ of this request depends on $@. | tst.js:16:13:16:19 | tainted | URL | tst.js:12:29:12:35 | req.url | a user-provided value |
2+
| tst.js:18:5:18:24 | request.get(tainted) | The $@ of this request depends on $@. | tst.js:18:17:18:23 | tainted | URL | tst.js:12:29:12:35 | req.url | a user-provided value |
3+
| tst.js:22:5:22:20 | request(options) | The $@ of this request depends on $@. | tst.js:21:19:21:25 | tainted | URL | tst.js:12:29:12:35 | req.url | a user-provided value |
4+
| tst.js:24:5:24:32 | request ... ainted) | The $@ of this request depends on $@. | tst.js:24:13:24:31 | "http://" + tainted | URL | tst.js:12:29:12:35 | req.url | a user-provided value |
5+
| tst.js:26:5:26:43 | request ... ainted) | The $@ of this request depends on $@. | tst.js:26:13:26:42 | "http:/ ... tainted | URL | tst.js:12:29:12:35 | req.url | a user-provided value |
6+
| tst.js:28:5:28:44 | request ... ainted) | The $@ of this request depends on $@. | tst.js:28:13:28:43 | "http:/ ... tainted | URL | tst.js:12:29:12:35 | req.url | a user-provided value |
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Security/CWE-918/RequestForgery.ql
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import request from 'request';
2+
import requestPromise from 'request-promise';
3+
import superagent from 'superagent';
4+
import http from 'http';
5+
import express from 'express';
6+
import axios from 'axios';
7+
import got from 'got';
8+
import nodeFetch from 'node-fetch';
9+
import url from 'url';
10+
11+
var server = http.createServer(function(req, res) {
12+
var tainted = url.parse(req.url, true).query.url;
13+
14+
request("example.com"); // OK
15+
16+
request(tainted); // NOT OK
17+
18+
request.get(tainted); // NOT OK
19+
20+
var options = {};
21+
options.url = tainted;
22+
request(options); // NOT OK
23+
24+
request("http://" + tainted); // NOT OK
25+
26+
request("http://example.com" + tainted); // NOT OK
27+
28+
request("http://example.com/" + tainted); // NOT OK
29+
30+
request("http://example.com/?" + tainted); // OK
31+
})

0 commit comments

Comments
 (0)