Skip to content

Commit b17aeb6

Browse files
authored
Merge pull request #118 from esben-semmle/js/request-forgery
Approved by asger-semmle
2 parents 5fef916 + aaf1ac7 commit b17aeb6

File tree

21 files changed

+624
-58
lines changed

21 files changed

+624
-58
lines changed

change-notes/1.18/analysis-javascript.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,13 @@
1919
* Type inference for simple function calls has been improved. This may give additional results for queries that rely on type inference.
2020

2121
* Support for popular libraries has been improved. Consequently, queries may produce more results on code bases that use the following libraries:
22+
- [axios](https://github.com/axios/axios)
2223
- [bluebird](https://bluebirdjs.com)
2324
- [browserid-crypto](https://github.com/mozilla/browserid-crypto)
2425
- [compose-function](https://github.com/stoeffel/compose-function)
2526
- [cookie-parser](https://github.com/expressjs/cookie-parser)
2627
- [cookie-session](https://github.com/expressjs/cookie-session)
28+
- [cross-fetch](https://github.com/lquixada/cross-fetch)
2729
- [crypto-js](https://github.com/https://github.com/brix/crypto-js)
2830
- [deep-assign](https://github.com/sindresorhus/deep-assign)
2931
- [deep-extend](https://github.com/unclechu/node-deep-extend)
@@ -45,9 +47,11 @@
4547
- [fast-json-parse](https://github.com/mcollina/fast-json-parse)
4648
- [forge](https://github.com/digitalbazaar/forge)
4749
- [format-util](https://github.com/tmpfs/format-util)
50+
- [got](https://github.com/sindresorhus/got)
4851
- [global](https://github.com/Raynos/global)
4952
- [he](https://github.com/mathiasbynens/he)
5053
- [html-entities](https://github.com/mdevils/node-html-entities)
54+
- [isomorphic-fetch](https://github.com/matthew-andrews/isomorphic-fetch)
5155
- [jquery](https://jquery.com)
5256
- [js-extend](https://github.com/vmattos/js-extend)
5357
- [json-parse-better-errors](https://github.com/zkat/json-parse-better-errors)
@@ -63,6 +67,7 @@
6367
- [mixin-object](https://github.com/jonschlinkert/mixin-object)
6468
- [MySQL2](https://github.com/sidorares/node-mysql2)
6569
- [node.extend](https://github.com/dreamerslab/node.extend)
70+
- [node-fetch](https://github.com/bitinn/node-fetch)
6671
- [object-assign](https://github.com/sindresorhus/object-assign)
6772
- [object.assign](https://github.com/ljharb/object.assign)
6873
- [object.defaults](https://github.com/jonschlinkert/object.defaults)
@@ -71,13 +76,18 @@
7176
- [printj](https://github.com/SheetJS/printj)
7277
- [q](https://documentup.com/kriskowal/q/)
7378
- [ramda](https://ramdajs.com)
79+
- [request](https://github.com/request/request)
80+
- [request-promise](https://github.com/request/request-promise)
81+
- [request-promise-any](https://github.com/request/request-promise-any)
82+
- [request-promise-native](https://github.com/request/request-promise-native)
7483
- [React Native](https://facebook.github.io/react-native/)
7584
- [safe-json-parse](https://github.com/Raynos/safe-json-parse)
7685
- [sanitize](https://github.com/pocketly/node-sanitize)
7786
- [sanitizer](https://github.com/theSmaw/Caja-HTML-Sanitizer)
7887
- [smart-extend](https://github.com/danielkalen/smart-extend)
7988
- [sprintf.js](https://github.com/alexei/sprintf.js)
8089
- [string-template](https://github.com/Matt-Esch/string-template)
90+
- [superagent](https://github.com/visionmedia/superagent)
8191
- [underscore](https://underscorejs.org)
8292
- [util-extend](https://github.com/isaacs/util-extend)
8393
- [utils-merge](https://github.com/jaredhanson/utils-merge)
@@ -94,6 +104,7 @@
94104
| Clear-text logging of sensitive information (`js/clear-text-logging`) | security, external/cwe/cwe-312, external/cwe/cwe-315, external/cwe/cwe-359 | Highlights logging of sensitive information, indicating a violation of [CWE-312](https://cwe.mitre.org/data/definitions/312.html). Results shown on LGTM by default. |
95105
| Disabling Electron webSecurity (`js/disabling-electron-websecurity`) | security, frameworks/electron | Highlights Electron browser objects that are created with the `webSecurity` property set to false. Results shown on LGTM by default. |
96106
| Enabling Electron allowRunningInsecureContent (`js/enabling-electron-insecure-content`) | security, frameworks/electron | Highlights Electron browser objects that are created with the `allowRunningInsecureContent` property set to true. Results shown on LGTM by default. |
107+
| Uncontrolled data used in remote request (`js/request-forgery`) | security, external/cwe/cwe-918 | Highlights remote requests that are built from unsanitized user input, indicating a violation of [CWE-918](https://cwe.mitre.org/data/definitions/918.html). Results are not shown on LGTM by default. |
97108
| Use of externally-controlled format string (`js/tainted-format-string`) | security, external/cwe/cwe-134 | Highlights format strings containing user-provided data, indicating a violation of [CWE-134](https://cwe.mitre.org/data/definitions/134.html). Results shown on LGTM by default. |
98109

99110
## Changes to existing queries

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 an HTTP 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 facilitates an SSRF attack. The request
44+
<code>http.get(...)</code> is vulnerable since attackers can choose
45+
the value of <code>target</code> to be anything they want. 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 medium
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+
});

javascript/ql/src/javascript.qll

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ import semmle.javascript.frameworks.AWS
5454
import semmle.javascript.frameworks.Azure
5555
import semmle.javascript.frameworks.Babel
5656
import semmle.javascript.frameworks.ComposedFunctions
57+
import semmle.javascript.frameworks.ClientRequests
5758
import semmle.javascript.frameworks.Credentials
5859
import semmle.javascript.frameworks.CryptoLibraries
5960
import semmle.javascript.frameworks.DigitalOcean

0 commit comments

Comments
 (0)