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

Skip to content

Commit 3f0a326

Browse files
committed
[Java] CWE-348: Use of less trusted source
1 parent a9527fd commit 3f0a326

20 files changed

Lines changed: 885 additions & 10 deletions

File tree

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import javax.servlet.http.HttpServletRequest;
2+
import org.apache.commons.lang3.StringUtils;
3+
import org.slf4j.Logger;
4+
import org.slf4j.LoggerFactory;
5+
import org.springframework.stereotype.Controller;
6+
import org.springframework.web.bind.annotation.GetMapping;
7+
import org.springframework.web.bind.annotation.ResponseBody;
8+
9+
@Controller
10+
public class UseOfLessTrustedSource {
11+
12+
private static final Logger log = LoggerFactory.getLogger(UseOfLessTrustedSource.class);
13+
14+
@GetMapping(value = "bad1")
15+
@ResponseBody
16+
public String bad1(HttpServletRequest request) {
17+
String remoteAddr = "";
18+
if (request != null) {
19+
remoteAddr = request.getHeader("X-FORWARDED-FOR");
20+
remoteAddr = remoteAddr.split(",")[0];
21+
if (remoteAddr == null || "".equals(remoteAddr)) {
22+
remoteAddr = request.getRemoteAddr();
23+
}
24+
}
25+
return remoteAddr;
26+
}
27+
28+
@GetMapping(value = "bad2")
29+
public void bad2(HttpServletRequest request) {
30+
String ip = request.getHeader("X-Forwarded-For");
31+
32+
log.debug("getClientIP header X-Forwarded-For:{}", ip);
33+
34+
if (StringUtils.isBlank(ip) || StringUtils.equalsIgnoreCase("unknown", ip)) {
35+
ip = request.getHeader("Proxy-Client-IP");
36+
log.debug("getClientIP header Proxy-Client-IP:{}", ip);
37+
}
38+
if (StringUtils.isBlank(ip) || StringUtils.equalsIgnoreCase("unknown", ip)) {
39+
ip = request.getHeader("WL-Proxy-Client-IP");
40+
log.debug("getClientIP header WL-Proxy-Client-IP:{}", ip);
41+
}
42+
if (StringUtils.isBlank(ip) || StringUtils.equalsIgnoreCase("unknown", ip)) {
43+
ip = request.getHeader("HTTP_CLIENT_IP");
44+
log.debug("getClientIP header HTTP_CLIENT_IP:{}", ip);
45+
}
46+
if (StringUtils.isBlank(ip) || StringUtils.equalsIgnoreCase("unknown", ip)) {
47+
ip = request.getHeader("HTTP_X_FORWARDED_FOR");
48+
log.debug("getClientIP header HTTP_X_FORWARDED_FOR:{}", ip);
49+
}
50+
if (StringUtils.isBlank(ip) || StringUtils.equalsIgnoreCase("unknown", ip)) {
51+
ip = request.getHeader("X-Real-IP");
52+
log.debug("getClientIP header X-Real-IP:{}", ip);
53+
}
54+
if (StringUtils.isBlank(ip) || StringUtils.equalsIgnoreCase("unknown", ip)) {
55+
ip = request.getRemoteAddr();
56+
log.debug("getRemoteAddr IP:{}", ip);
57+
}
58+
System.out.println("client ip is: " + ip);
59+
}
60+
61+
@GetMapping(value = "good1")
62+
@ResponseBody
63+
public String good1(HttpServletRequest request) {
64+
String remoteAddr = "";
65+
if (request != null) {
66+
remoteAddr = request.getHeader("X-FORWARDED-FOR");
67+
remoteAddr = remoteAddr.split(",")[remoteAddr.split(",").length - 1]; // good
68+
if (remoteAddr == null || "".equals(remoteAddr)) {
69+
remoteAddr = request.getRemoteAddr();
70+
}
71+
}
72+
return remoteAddr;
73+
}
74+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<!DOCTYPE qhelp PUBLIC
2+
"-//Semmle//qhelp//EN"
3+
"qhelp.dtd">
4+
<qhelp>
5+
<overview>
6+
<p>The software obtains the original client IP address through the http header <code>X-Forwarded-For</code>, which is used to ensure
7+
security or track it in the log for statistical or other reasons. Attackers can use <code>X-Forwarded-For </code> Spoofing software.</p>
8+
9+
</overview>
10+
<recommendation>
11+
12+
<p>When the software is not using a proxy server, get the last ip.</p>
13+
14+
</recommendation>
15+
<example>
16+
17+
<p>The following examples show the bad case and the good case respectively. Bad case, such as <code>bad1</code> to <code>bad2</code>.
18+
In the <code>bad1</code> method, the value of <code>X-Forwarded-For</code> in <code>header</code> is split, and the first value of
19+
the split array is obtained. Good case, such as <code>good1</code>, split the value of <code>X-Forwarded-For</code> in <code>header</code>
20+
and get the last value of the split array.</p>
21+
22+
<sample src="UseOfLessTrustedSource.java" />
23+
24+
</example>
25+
<references>
26+
27+
<li>Prevent IP address spoofing with X-Forwarded-For header when using AWS ELB and Clojure Ring:
28+
<a href="https://www.dennis-schneider.com/blog/prevent-ip-address-spoofing-with-x-forwarded-for-header-and-aws-elb-in-clojure-ring/">
29+
Prevent IP address spoofing with...</a>
30+
</li>
31+
32+
<li>Security Rule Zero: A Warning about X-Forwarded-For:
33+
<a href="https://www.f5.com/company/blog/security-rule-zero-a-warning-about-x-forwarded-for">
34+
Security Rule Zero: A Warning about X-Forwarded-For</a>
35+
</li>
36+
37+
</references>
38+
</qhelp>
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/**
2+
* @name X-Forwarded-For spoofing
3+
* @description The software obtains the client ip through `X-Forwarded-For`,
4+
* and the attacker can modify the value of `X-Forwarded-For` to forge the ip.
5+
* @kind path-problem
6+
* @problem.severity error
7+
* @precision high
8+
* @id java/use-of-less-trusted-source
9+
* @tags security
10+
* external/cwe/cwe-348
11+
*/
12+
13+
import java
14+
import UseOfLessTrustedSourceLib
15+
import semmle.code.java.dataflow.FlowSources
16+
import DataFlow::PathGraph
17+
18+
class UseOfLessTrustedSourceConfig extends TaintTracking::Configuration {
19+
UseOfLessTrustedSourceConfig() { this = "UseOfLessTrustedSourceConfig" }
20+
21+
override predicate isSource(DataFlow::Node source) { source instanceof UseOfLessTrustedSource }
22+
23+
override predicate isSink(DataFlow::Node sink) { sink instanceof UseOfLessTrustedSink }
24+
25+
/**
26+
* When using `,` split request data and not taking the first value of
27+
* the array, it is considered as `good`.
28+
*/
29+
override predicate isSanitizer(DataFlow::Node node) {
30+
exists(ArrayAccess aa, MethodAccess ma | aa.getArray() = ma |
31+
ma.getQualifier() = node.asExpr() and
32+
ma.getMethod() instanceof SplitMethod and
33+
not aa.getIndexExpr().toString() = "0"
34+
)
35+
}
36+
}
37+
38+
from DataFlow::PathNode source, DataFlow::PathNode sink, UseOfLessTrustedSourceConfig conf
39+
where
40+
conf.hasFlowPath(source, sink) and
41+
source.getNode().getEnclosingCallable() = sink.getNode().getEnclosingCallable() and
42+
xffIsFirstGet(source.getNode())
43+
select sink.getNode(), source, sink, "X-Forwarded-For spoofing might include code from $@.",
44+
source.getNode(), "this user input"
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
import java
2+
import DataFlow
3+
import semmle.code.java.dataflow.DataFlow
4+
import experimental.semmle.code.java.Logging
5+
6+
/** A data flow source of the value of the `x-forwarded-for` field in the `header`. */
7+
class UseOfLessTrustedSource extends DataFlow::Node {
8+
UseOfLessTrustedSource() {
9+
exists(MethodAccess ma |
10+
ma.getMethod().hasName("getHeader") and
11+
ma.getArgument(0).toString().toLowerCase() = "\"x-forwarded-for\"" and
12+
ma = this.asExpr()
13+
)
14+
}
15+
}
16+
17+
/** A data flow sink of method return or log output or local print. */
18+
class UseOfLessTrustedSink extends DataFlow::Node {
19+
UseOfLessTrustedSink() {
20+
exists(ReturnStmt rs | rs.getResult() = this.asExpr())
21+
or
22+
exists(MethodAccess ma |
23+
ma.getMethod().getName() in ["print", "println"] and
24+
(
25+
ma.getMethod().getDeclaringType().hasQualifiedName("java.io", "PrintWriter") or
26+
ma.getMethod().getDeclaringType().hasQualifiedName("java.io", "PrintStream")
27+
) and
28+
ma.getAnArgument() = this.asExpr()
29+
)
30+
or
31+
exists(LoggingCall lc | lc.getAnArgument() = this.asExpr())
32+
}
33+
}
34+
35+
/** A method that split string. */
36+
class SplitMethod extends Method {
37+
SplitMethod() {
38+
this.getNumberOfParameters() = 1 and
39+
this.hasQualifiedName("java.lang", "String", "split")
40+
}
41+
}
42+
43+
/**
44+
* A call to the ServletRequest.getHeader method and the argument are
45+
* `wl-proxy-client-ip`/`proxy-client-ip`/`http_client_ip`/`http_x_forwarded_for`/`x-real-ip`.
46+
*/
47+
class HeaderIpCall extends MethodAccess {
48+
HeaderIpCall() {
49+
this.getMethod().hasName("getHeader") and
50+
this.getMethod()
51+
.getDeclaringType()
52+
.getASupertype*()
53+
.hasQualifiedName("javax.servlet", "ServletRequest") and
54+
this.getArgument(0).toString().toLowerCase() in [
55+
"\"wl-proxy-client-ip\"", "\"proxy-client-ip\"", "\"http_client_ip\"",
56+
"\"http_x_forwarded_for\"", "\"x-real-ip\""
57+
]
58+
}
59+
}
60+
61+
/** A call to `ServletRequest.getRemoteAddr` method. */
62+
class RemoteAddrCall extends MethodAccess {
63+
RemoteAddrCall() {
64+
this.getMethod().hasName("getRemoteAddr") and
65+
this.getMethod()
66+
.getDeclaringType()
67+
.getASupertype*()
68+
.hasQualifiedName("javax.servlet", "ServletRequest")
69+
}
70+
}
71+
72+
/** The first one in the method to get the ip value through `x-forwarded-for`. */
73+
predicate xffIsFirstGet(Node node) {
74+
exists(HeaderIpCall hic |
75+
node.getEnclosingCallable() = hic.getEnclosingCallable() and
76+
node.getLocation().getEndLine() < hic.getLocation().getEndLine()
77+
)
78+
or
79+
exists(RemoteAddrCall rac |
80+
node.getEnclosingCallable() = rac.getEnclosingCallable() and
81+
node.getLocation().getEndLine() < rac.getLocation().getEndLine()
82+
)
83+
or
84+
not exists(HeaderIpCall hic, RemoteAddrCall rac |
85+
node.getEnclosingCallable() = hic.getEnclosingCallable() and
86+
node.getEnclosingCallable() = rac.getEnclosingCallable()
87+
)
88+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
edges
2+
| UseOfLessTrustedSource.java:19:26:19:61 | getHeader(...) : String | UseOfLessTrustedSource.java:25:16:25:25 | remoteAddr |
3+
| UseOfLessTrustedSource.java:30:21:30:56 | getHeader(...) : String | UseOfLessTrustedSource.java:32:60:32:61 | ip |
4+
| UseOfLessTrustedSource.java:30:21:30:56 | getHeader(...) : String | UseOfLessTrustedSource.java:58:28:58:48 | ... + ... |
5+
nodes
6+
| UseOfLessTrustedSource.java:19:26:19:61 | getHeader(...) : String | semmle.label | getHeader(...) : String |
7+
| UseOfLessTrustedSource.java:25:16:25:25 | remoteAddr | semmle.label | remoteAddr |
8+
| UseOfLessTrustedSource.java:30:21:30:56 | getHeader(...) : String | semmle.label | getHeader(...) : String |
9+
| UseOfLessTrustedSource.java:32:60:32:61 | ip | semmle.label | ip |
10+
| UseOfLessTrustedSource.java:58:28:58:48 | ... + ... | semmle.label | ... + ... |
11+
#select
12+
| UseOfLessTrustedSource.java:25:16:25:25 | remoteAddr | UseOfLessTrustedSource.java:19:26:19:61 | getHeader(...) : String | UseOfLessTrustedSource.java:25:16:25:25 | remoteAddr | X-Forwarded-For spoofing might include code from $@. | UseOfLessTrustedSource.java:19:26:19:61 | getHeader(...) | this user input |
13+
| UseOfLessTrustedSource.java:32:60:32:61 | ip | UseOfLessTrustedSource.java:30:21:30:56 | getHeader(...) : String | UseOfLessTrustedSource.java:32:60:32:61 | ip | X-Forwarded-For spoofing might include code from $@. | UseOfLessTrustedSource.java:30:21:30:56 | getHeader(...) | this user input |
14+
| UseOfLessTrustedSource.java:58:28:58:48 | ... + ... | UseOfLessTrustedSource.java:30:21:30:56 | getHeader(...) : String | UseOfLessTrustedSource.java:58:28:58:48 | ... + ... | X-Forwarded-For spoofing might include code from $@. | UseOfLessTrustedSource.java:30:21:30:56 | getHeader(...) | this user input |
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import javax.servlet.http.HttpServletRequest;
2+
import org.apache.commons.lang3.StringUtils;
3+
import org.slf4j.Logger;
4+
import org.slf4j.LoggerFactory;
5+
import org.springframework.stereotype.Controller;
6+
import org.springframework.web.bind.annotation.GetMapping;
7+
import org.springframework.web.bind.annotation.ResponseBody;
8+
9+
@Controller
10+
public class UseOfLessTrustedSource {
11+
12+
private static final Logger log = LoggerFactory.getLogger(UseOfLessTrustedSource.class);
13+
14+
@GetMapping(value = "bad1")
15+
@ResponseBody
16+
public String bad1(HttpServletRequest request) {
17+
String remoteAddr = "";
18+
if (request != null) {
19+
remoteAddr = request.getHeader("X-FORWARDED-FOR");
20+
remoteAddr = remoteAddr.split(",")[0];
21+
if (remoteAddr == null || "".equals(remoteAddr)) {
22+
remoteAddr = request.getRemoteAddr();
23+
}
24+
}
25+
return remoteAddr;
26+
}
27+
28+
@GetMapping(value = "bad2")
29+
public void bad2(HttpServletRequest request) {
30+
String ip = request.getHeader("X-Forwarded-For");
31+
32+
log.debug("getClientIP header X-Forwarded-For:{}", ip);
33+
34+
if (StringUtils.isBlank(ip) || StringUtils.equalsIgnoreCase("unknown", ip)) {
35+
ip = request.getHeader("Proxy-Client-IP");
36+
log.debug("getClientIP header Proxy-Client-IP:{}", ip);
37+
}
38+
if (StringUtils.isBlank(ip) || StringUtils.equalsIgnoreCase("unknown", ip)) {
39+
ip = request.getHeader("WL-Proxy-Client-IP");
40+
log.debug("getClientIP header WL-Proxy-Client-IP:{}", ip);
41+
}
42+
if (StringUtils.isBlank(ip) || StringUtils.equalsIgnoreCase("unknown", ip)) {
43+
ip = request.getHeader("HTTP_CLIENT_IP");
44+
log.debug("getClientIP header HTTP_CLIENT_IP:{}", ip);
45+
}
46+
if (StringUtils.isBlank(ip) || StringUtils.equalsIgnoreCase("unknown", ip)) {
47+
ip = request.getHeader("HTTP_X_FORWARDED_FOR");
48+
log.debug("getClientIP header HTTP_X_FORWARDED_FOR:{}", ip);
49+
}
50+
if (StringUtils.isBlank(ip) || StringUtils.equalsIgnoreCase("unknown", ip)) {
51+
ip = request.getHeader("X-Real-IP");
52+
log.debug("getClientIP header X-Real-IP:{}", ip);
53+
}
54+
if (StringUtils.isBlank(ip) || StringUtils.equalsIgnoreCase("unknown", ip)) {
55+
ip = request.getRemoteAddr();
56+
log.debug("getRemoteAddr IP:{}", ip);
57+
}
58+
System.out.println("client ip is: " + ip);
59+
}
60+
61+
@GetMapping(value = "good1")
62+
@ResponseBody
63+
public String good1(HttpServletRequest request) {
64+
String remoteAddr = "";
65+
if (request != null) {
66+
remoteAddr = request.getHeader("X-FORWARDED-FOR");
67+
remoteAddr = remoteAddr.split(",")[remoteAddr.split(",").length - 1]; // good
68+
if (remoteAddr == null || "".equals(remoteAddr)) {
69+
remoteAddr = request.getRemoteAddr();
70+
}
71+
}
72+
return remoteAddr;
73+
}
74+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
experimental/Security/CWE/CWE-348/UseOfLessTrustedSource.ql
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
//semmle-extractor-options: --javac-args -cp ${testdir}/../../../../stubs/servlet-api-2.4:${testdir}/../../../../stubs/springframework-5.2.3/:${tes
2+
tdir}/../../../../stubs/slf4j-api-1.6.4/:${testdir}/../../../../stubs/apache-commons-lang3-3.7/

0 commit comments

Comments
 (0)