Skip to content

Commit f3c0bf7

Browse files
committed
copy-paste from our repo
1 parent d7c2979 commit f3c0bf7

File tree

3 files changed

+127
-0
lines changed

3 files changed

+127
-0
lines changed
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<!DOCTYPE qhelp PUBLIC
2+
"-//Semmle//qhelp//EN"
3+
"qhelp.dtd">
4+
<qhelp>
5+
6+
<overview>
7+
<p>
8+
Directly incorporating user input into an HTTP request without validating the input can facilitate
9+
server side request forgery attacks, where the attacker essentially controls the request.
10+
</p>
11+
</overview>
12+
13+
<recommendation>
14+
<p>
15+
To guard against server side request forgery, it is advisable to avoid putting user input directly into a
16+
network request. If using user input is necessary, then is mandatory to validate them. Only allow numeric and alphanumeric values.
17+
URL encoding is not a solution in certain scenarios, such as, an architecture build over NGINX proxies.
18+
</p>
19+
</recommendation>
20+
21+
<example>
22+
<p>
23+
The following example shows an HTTP request parameter being used directly in a URL request without
24+
validating the input, which facilitates an SSRF attack. The request <code>axios.get("https://example.com/current_api/"+target)</code> is
25+
vulnerable since attackers can choose the value of <code>target</code> to be anything they want. For
26+
instance, the attacker can choose <code>"../super_secret_api"</code> as the target, causing the
27+
URL to become <code>"https://example.com/super_secret_api"</code>.
28+
</p>
29+
30+
<p>
31+
A request to <code>https://example.com/super_secret_api</code> may be problematic if that api is not
32+
meant to be directly accessible from the attacker's machine.
33+
</p>
34+
35+
<sample src="SSRF.js"/>
36+
37+
<p>
38+
One way to remedy the problem is to validate the user input to only allow alphanumeric values:
39+
</p>
40+
41+
<sample src="SSRFGood.js"/>
42+
</example>
43+
44+
<references>
45+
46+
<li>OWASP: <a href="https://www.owasp.org/www-community/attacks/Server_Side_Request_Forgery">SSRF</a></li>
47+
48+
</references>
49+
</qhelp>
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/**
2+
* @id javascript/ssrf-path
3+
* @kind path-problem
4+
* @name Uncontrolled data used in network request
5+
* @description Sending network requests with user-controlled data as part of the URL allows for request forgery attacks.
6+
* @problem.severity error
7+
* @precision medium
8+
* @tags security
9+
* external/cwe/cwe-918
10+
*/
11+
12+
import javascript
13+
import SSRF
14+
import DataFlow::PathGraph
15+
16+
from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink, DataFlow::Node request
17+
where cfg.hasFlowPath(source, sink) and request = sink.getNode().(RequestForgery::Sink).getARequest()
18+
select sink, source, sink, "The URL of this request depends on a user-provided value"
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import javascript
2+
3+
import semmle.javascript.security.dataflow.RequestForgeryCustomizations
4+
import semmle.javascript.security.dataflow.UrlConcatenation
5+
6+
class Configuration extends TaintTracking::Configuration {
7+
Configuration() { this = "SSRF" }
8+
9+
override predicate isSource(DataFlow::Node source) { source instanceof RequestForgery::Source }
10+
11+
override predicate isSink(DataFlow::Node sink) { sink instanceof RequestForgery::Sink }
12+
13+
override predicate isSanitizer(DataFlow::Node node) {
14+
super.isSanitizer(node) or
15+
node instanceof RequestForgery::Sanitizer
16+
}
17+
18+
override predicate isSanitizerEdge(DataFlow::Node source, DataFlow::Node sink) {
19+
sanitizingPrefixEdge(source, sink)
20+
}
21+
override predicate isSanitizerGuard(TaintTracking::SanitizerGuardNode nd) {
22+
nd instanceof IntegerCheck or
23+
nd instanceof ValidatorCheck
24+
}
25+
}
26+
27+
/**
28+
* Number.isInteger is a sanitizer guard because a number can't be used to exploit a SSRF.
29+
*/
30+
class IntegerCheck extends TaintTracking::SanitizerGuardNode, DataFlow::CallNode{
31+
IntegerCheck() {
32+
this = DataFlow::globalVarRef("Number").getAMemberCall("isInteger")
33+
}
34+
35+
override predicate sanitizes(boolean outcome, Expr e){
36+
outcome = true and
37+
e = getArgument(0).asExpr()
38+
}
39+
}
40+
41+
/**
42+
* ValidatorCheck identifies if exists a call to validator's library methods.
43+
* validator is a library which has a variety of input-validation functions. We are interesed in
44+
* checking that source is a number (any type of number) or an alphanumeric value.
45+
*/
46+
class ValidatorCheck extends TaintTracking::SanitizerGuardNode, DataFlow::CallNode {
47+
ValidatorCheck() {
48+
exists(
49+
DataFlow::SourceNode mod, string method |
50+
mod = DataFlow::moduleImport("validator") and
51+
this = mod.getAChainedMethodCall(method)
52+
and method in ["isAlphanumeric", "isAlpha", "isDecimal", "isFloat",
53+
"isHexadecimal", "isHexColor", "isInt", "isNumeric", "isOctal", "isUUID"]
54+
)
55+
}
56+
override predicate sanitizes(boolean outcome, Expr e){
57+
outcome = true and
58+
e = getArgument(0).asExpr()
59+
}
60+
}

0 commit comments

Comments
 (0)