From 8296abcea84e89166608fa603025101c4c8eca47 Mon Sep 17 00:00:00 2001 From: haby0 Date: Mon, 19 Apr 2021 20:59:47 +0800 Subject: [PATCH] Fix Modify the ql query (the qhelp part is not modified). --- .../CWE/CWE-348/UseOfLessTrustedSource.java | 38 +++-- .../CWE/CWE-348/UseOfLessTrustedSource.qhelp | 9 +- .../CWE/CWE-348/UseOfLessTrustedSource.ql | 27 ++-- .../CWE/CWE-348/UseOfLessTrustedSourceLib.qll | 146 +++++++++++------- .../CWE-348/UseOfLessTrustedSource.expected | 56 +++++-- .../CWE-348/UseOfLessTrustedSource.java | 38 +++-- .../query-tests/security/CWE-348/options | 3 +- .../beans/factory/annotation/Autowired.java | 14 ++ 8 files changed, 213 insertions(+), 118 deletions(-) create mode 100644 java/ql/test/stubs/springframework-5.2.3/org/springframework/beans/factory/annotation/Autowired.java diff --git a/java/ql/src/experimental/Security/CWE/CWE-348/UseOfLessTrustedSource.java b/java/ql/src/experimental/Security/CWE/CWE-348/UseOfLessTrustedSource.java index d338134ed3e..22d7100223c 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-348/UseOfLessTrustedSource.java +++ b/java/ql/src/experimental/Security/CWE/CWE-348/UseOfLessTrustedSource.java @@ -2,6 +2,7 @@ import javax.servlet.http.HttpServletRequest; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.ResponseBody; @@ -11,24 +12,13 @@ public class UseOfLessTrustedSource { private static final Logger log = LoggerFactory.getLogger(UseOfLessTrustedSource.class); - @GetMapping(value = "bad1") - @ResponseBody - public String bad1(HttpServletRequest request) { - String remoteAddr = ""; - if (request != null) { - remoteAddr = request.getHeader("X-FORWARDED-FOR"); - remoteAddr = remoteAddr.split(",")[0]; - if (remoteAddr == null || "".equals(remoteAddr)) { - remoteAddr = request.getRemoteAddr(); - } - } - return remoteAddr; - } + @Autowired + private HttpServletRequest request; - @GetMapping(value = "bad2") - public void bad2(HttpServletRequest request) { + @GetMapping(value = "bad1") + public void bad1(HttpServletRequest request) { String ip = request.getHeader("X-Forwarded-For"); - + log.debug("getClientIP header X-Forwarded-For:{}", ip); if (StringUtils.isBlank(ip) || StringUtils.equalsIgnoreCase("unknown", ip)) { @@ -58,6 +48,14 @@ public class UseOfLessTrustedSource { System.out.println("client ip is: " + ip); } + @GetMapping(value = "bad2") + public void bad2(HttpServletRequest request) { + String ip = getClientIP(); + if (!StringUtils.startsWith(ip, "192.168.")) { + new Exception("ip illegal"); + } + } + @GetMapping(value = "good1") @ResponseBody public String good1(HttpServletRequest request) { @@ -71,4 +69,12 @@ public class UseOfLessTrustedSource { } return remoteAddr; } + + protected String getClientIP() { + String xfHeader = request.getHeader("X-Forwarded-For"); + if (xfHeader == null) { + return request.getRemoteAddr(); + } + return xfHeader.split(",")[0]; + } } \ No newline at end of file diff --git a/java/ql/src/experimental/Security/CWE/CWE-348/UseOfLessTrustedSource.qhelp b/java/ql/src/experimental/Security/CWE/CWE-348/UseOfLessTrustedSource.qhelp index 1c3a4485cbd..6b2eb2e7eed 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-348/UseOfLessTrustedSource.qhelp +++ b/java/ql/src/experimental/Security/CWE/CWE-348/UseOfLessTrustedSource.qhelp @@ -24,14 +24,11 @@ and get the last value of the split array.

-
  • Prevent IP address spoofing with X-Forwarded-For header when using AWS ELB and Clojure Ring: - -Prevent IP address spoofing with... +
  • Dennis Schneider: +Prevent IP address spoofing with X-Forwarded-For header when using AWS ELB and Clojure Ring
  • -
  • Security Rule Zero: A Warning about X-Forwarded-For: - -Security Rule Zero: A Warning about X-Forwarded-For +
  • Security Rule Zero: A Warning about X-Forwarded-For
  • diff --git a/java/ql/src/experimental/Security/CWE/CWE-348/UseOfLessTrustedSource.ql b/java/ql/src/experimental/Security/CWE/CWE-348/UseOfLessTrustedSource.ql index 15d665d37ab..3c3feeb8f03 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-348/UseOfLessTrustedSource.ql +++ b/java/ql/src/experimental/Security/CWE/CWE-348/UseOfLessTrustedSource.ql @@ -1,20 +1,23 @@ /** - * @name X-Forwarded-For spoofing + * @name IP address spoofing * @description The software obtains the client ip through `X-Forwarded-For`, * and the attacker can modify the value of `X-Forwarded-For` to forge the ip. * @kind path-problem * @problem.severity error * @precision high - * @id java/use-of-less-trusted-source + * @id java/ip-address-spoofing * @tags security * external/cwe/cwe-348 */ import java import UseOfLessTrustedSourceLib +import semmle.code.java.dataflow.DataFlow2 +import semmle.code.java.dataflow.TaintTracking2 import semmle.code.java.dataflow.FlowSources import DataFlow::PathGraph +/** Taint-tracking configuration tracing flow from get method request sources to output jsonp data. */ class UseOfLessTrustedSourceConfig extends TaintTracking::Configuration { UseOfLessTrustedSourceConfig() { this = "UseOfLessTrustedSourceConfig" } @@ -23,22 +26,26 @@ class UseOfLessTrustedSourceConfig extends TaintTracking::Configuration { override predicate isSink(DataFlow::Node sink) { sink instanceof UseOfLessTrustedSink } /** - * When using `,` split request data and not taking the first value of - * the array, it is considered as `good`. + * When using `,` split request data and not taking the first value of the array, it is considered as `good`. */ override predicate isSanitizer(DataFlow::Node node) { exists(ArrayAccess aa, MethodAccess ma | aa.getArray() = ma | ma.getQualifier() = node.asExpr() and ma.getMethod() instanceof SplitMethod and - not aa.getIndexExpr().toString() = "0" + not aa.getIndexExpr().(CompileTimeConstantExpr).getIntValue() = 0 + ) + } + + override predicate isAdditionalTaintStep(DataFlow::Node prod, DataFlow::Node succ) { + exists(MethodAccess ma | + ma.getAnArgument() = prod.asExpr() and + ma = succ.asExpr() and + ma.getMethod().getReturnType() instanceof BooleanType ) } } from DataFlow::PathNode source, DataFlow::PathNode sink, UseOfLessTrustedSourceConfig conf -where - conf.hasFlowPath(source, sink) and - source.getNode().getEnclosingCallable() = sink.getNode().getEnclosingCallable() and - xffIsFirstGet(source.getNode()) -select sink.getNode(), source, sink, "X-Forwarded-For spoofing might include code from $@.", +where conf.hasFlowPath(source, sink) +select sink.getNode(), source, sink, "IP address spoofing might include code from $@.", source.getNode(), "this user input" diff --git a/java/ql/src/experimental/Security/CWE/CWE-348/UseOfLessTrustedSourceLib.qll b/java/ql/src/experimental/Security/CWE/CWE-348/UseOfLessTrustedSourceLib.qll index 0598f843840..ac72be64f5d 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-348/UseOfLessTrustedSourceLib.qll +++ b/java/ql/src/experimental/Security/CWE/CWE-348/UseOfLessTrustedSourceLib.qll @@ -1,24 +1,105 @@ import java import DataFlow -import semmle.code.java.dataflow.DataFlow +import semmle.code.java.dataflow.TaintTracking2 +import semmle.code.java.security.QueryInjection import experimental.semmle.code.java.Logging -/** A data flow source of the value of the `x-forwarded-for` field in the `header`. */ +/** + * A data flow source of the client ip obtained according to the remote endpoint identifier specified + * in the header (`X-Forwarded-For`, `X-Real-IP`, `Proxy-Client-IP`, etc.). + * + * For example: `ServletRequest.getHeader("X-Forwarded-For")`. + */ class UseOfLessTrustedSource extends DataFlow::Node { UseOfLessTrustedSource() { exists(MethodAccess ma | ma.getMethod().hasName("getHeader") and - ma.getArgument(0).toString().toLowerCase() = "\"x-forwarded-for\"" and + ma.getArgument(0).(CompileTimeConstantExpr).getStringValue().toLowerCase() in [ + "x-forwarded-for", "x-real-ip", "proxy-client-ip", "wl-proxy-client-ip", + "http_x_forwarded_for", "http_x_forwarded", "http_x_cluster_client_ip", "http_client_ip", + "http_forwarded_for", "http_forwarded", "http_via", "remote_addr" + ] and ma = this.asExpr() ) } } -/** A data flow sink of method return or log output or local print. */ -class UseOfLessTrustedSink extends DataFlow::Node { - UseOfLessTrustedSink() { - exists(ReturnStmt rs | rs.getResult() = this.asExpr()) - or +/** A data flow sink for ip address forgery vulnerabilities. */ +abstract class UseOfLessTrustedSink extends DataFlow::Node { } + +/** + * A data flow sink for the if condition, which does not include the null judgment of the remote client ip address. + * + * For example: `if (!StringUtils.startsWith(ipAddr, "192.168.")){...` determine whether the client ip starts + * with `192.168.`, and the program can be deceived by forging the ip address. + * `if (remoteAddr == null || "".equals(remoteAddr)) {...` judging whether the client ip is a null value, + * it needs to be excluded + */ +private class IfConditionSink extends UseOfLessTrustedSink { + IfConditionSink() { + exists(IfStmt is | + is.getCondition() = this.asExpr() and + not exists(EQExpr eqe | + eqe.getAnOperand() instanceof NullLiteral and + is.getCondition() = eqe.getParent*() + ) and + not exists(NEExpr nee | + nee.getAnOperand() instanceof NullLiteral and + is.getCondition() = nee.getParent*() + ) and + not exists(MethodAccess ma | + ma.getMethod().hasName("equals") and + ma.getMethod().getNumberOfParameters() = 1 and + ( + ma.getQualifier().(CompileTimeConstantExpr).getStringValue() = "" or + ma.getArgument(0).(CompileTimeConstantExpr).getStringValue() = "" + ) and + is.getCondition() = ma.getParent*() + ) and + not exists(MethodAccess ma | + ma.getMethod().hasName("equalsIgnoreCase") and + ma.getMethod().getNumberOfParameters() = 1 and + ( + ma.getQualifier().(CompileTimeConstantExpr).getStringValue() = "unknown" or + ma.getArgument(0).(CompileTimeConstantExpr).getStringValue() = "unknown" + ) and + is.getCondition() = ma.getParent*() + ) and + not exists(MethodAccess ma | + ma.getMethod().getName() in ["isEmpty", "isNotEmpty"] and + ma.getMethod().getNumberOfParameters() = 1 and + is.getCondition() = ma.getParent*() + ) and + not exists(MethodAccess ma | + ( + ma.getMethod().hasQualifiedName("org.apache.commons.lang3", "StringUtils", "isBlank") or + ma.getMethod().hasQualifiedName("org.apache.commons.lang3", "StringUtils", "isNotBlank") + ) and + is.getCondition() = ma.getParent*() + ) and + not exists(MethodAccess ma | + ma.getMethod() + .hasQualifiedName("org.apache.commons.lang3", "StringUtils", "equalsIgnoreCase") and + ma.getAnArgument().(CompileTimeConstantExpr).getStringValue() = "unknown" and + is.getCondition() = ma.getParent*() + ) + ) + } +} + +/** A data flow sink for sql operation. */ +private class SqlOperationSink extends UseOfLessTrustedSink { + SqlOperationSink() { this instanceof QueryInjectionSink } +} + +/** A data flow sink for log operation. */ +private class LogOperationSink extends UseOfLessTrustedSink { + LogOperationSink() { exists(LoggingCall lc | lc.getAnArgument() = this.asExpr()) } +} + +/** A data flow sink for local output. */ +private class PrintSink extends UseOfLessTrustedSink { + PrintSink() { exists(MethodAccess ma | ma.getMethod().getName() in ["print", "println"] and ( @@ -27,8 +108,6 @@ class UseOfLessTrustedSink extends DataFlow::Node { ) and ma.getAnArgument() = this.asExpr() ) - or - exists(LoggingCall lc | lc.getAnArgument() = this.asExpr()) } } @@ -39,50 +118,3 @@ class SplitMethod extends Method { this.hasQualifiedName("java.lang", "String", "split") } } - -/** - * A call to the ServletRequest.getHeader method and the argument are - * `wl-proxy-client-ip`/`proxy-client-ip`/`http_client_ip`/`http_x_forwarded_for`/`x-real-ip`. - */ -class HeaderIpCall extends MethodAccess { - HeaderIpCall() { - this.getMethod().hasName("getHeader") and - this.getMethod() - .getDeclaringType() - .getASupertype*() - .hasQualifiedName("javax.servlet", "ServletRequest") and - this.getArgument(0).toString().toLowerCase() in [ - "\"wl-proxy-client-ip\"", "\"proxy-client-ip\"", "\"http_client_ip\"", - "\"http_x_forwarded_for\"", "\"x-real-ip\"" - ] - } -} - -/** A call to `ServletRequest.getRemoteAddr` method. */ -class RemoteAddrCall extends MethodAccess { - RemoteAddrCall() { - this.getMethod().hasName("getRemoteAddr") and - this.getMethod() - .getDeclaringType() - .getASupertype*() - .hasQualifiedName("javax.servlet", "ServletRequest") - } -} - -/** The first one in the method to get the ip value through `x-forwarded-for`. */ -predicate xffIsFirstGet(Node node) { - exists(HeaderIpCall hic | - node.getEnclosingCallable() = hic.getEnclosingCallable() and - node.getLocation().getEndLine() < hic.getLocation().getEndLine() - ) - or - exists(RemoteAddrCall rac | - node.getEnclosingCallable() = rac.getEnclosingCallable() and - node.getLocation().getEndLine() < rac.getLocation().getEndLine() - ) - or - not exists(HeaderIpCall hic, RemoteAddrCall rac | - node.getEnclosingCallable() = hic.getEnclosingCallable() and - node.getEnclosingCallable() = rac.getEnclosingCallable() - ) -} diff --git a/java/ql/test/experimental/query-tests/security/CWE-348/UseOfLessTrustedSource.expected b/java/ql/test/experimental/query-tests/security/CWE-348/UseOfLessTrustedSource.expected index a71ad9629ce..65bbc8d218b 100644 --- a/java/ql/test/experimental/query-tests/security/CWE-348/UseOfLessTrustedSource.expected +++ b/java/ql/test/experimental/query-tests/security/CWE-348/UseOfLessTrustedSource.expected @@ -1,14 +1,48 @@ edges -| UseOfLessTrustedSource.java:19:26:19:61 | getHeader(...) : String | UseOfLessTrustedSource.java:25:16:25:25 | remoteAddr | -| UseOfLessTrustedSource.java:30:21:30:56 | getHeader(...) : String | UseOfLessTrustedSource.java:32:60:32:61 | ip | -| UseOfLessTrustedSource.java:30:21:30:56 | getHeader(...) : String | UseOfLessTrustedSource.java:58:28:58:48 | ... + ... | +| UseOfLessTrustedSource.java:20:21:20:56 | getHeader(...) : String | UseOfLessTrustedSource.java:22:60:22:61 | ip | +| UseOfLessTrustedSource.java:20:21:20:56 | getHeader(...) : String | UseOfLessTrustedSource.java:48:28:48:48 | ... + ... | +| UseOfLessTrustedSource.java:25:18:25:53 | getHeader(...) : String | UseOfLessTrustedSource.java:26:64:26:65 | ip | +| UseOfLessTrustedSource.java:25:18:25:53 | getHeader(...) : String | UseOfLessTrustedSource.java:48:28:48:48 | ... + ... | +| UseOfLessTrustedSource.java:29:18:29:56 | getHeader(...) : String | UseOfLessTrustedSource.java:30:67:30:68 | ip | +| UseOfLessTrustedSource.java:29:18:29:56 | getHeader(...) : String | UseOfLessTrustedSource.java:48:28:48:48 | ... + ... | +| UseOfLessTrustedSource.java:33:18:33:52 | getHeader(...) : String | UseOfLessTrustedSource.java:34:63:34:64 | ip | +| UseOfLessTrustedSource.java:33:18:33:52 | getHeader(...) : String | UseOfLessTrustedSource.java:48:28:48:48 | ... + ... | +| UseOfLessTrustedSource.java:37:18:37:58 | getHeader(...) : String | UseOfLessTrustedSource.java:38:69:38:70 | ip | +| UseOfLessTrustedSource.java:37:18:37:58 | getHeader(...) : String | UseOfLessTrustedSource.java:48:28:48:48 | ... + ... | +| UseOfLessTrustedSource.java:41:18:41:47 | getHeader(...) : String | UseOfLessTrustedSource.java:42:58:42:59 | ip | +| UseOfLessTrustedSource.java:41:18:41:47 | getHeader(...) : String | UseOfLessTrustedSource.java:48:28:48:48 | ... + ... | +| UseOfLessTrustedSource.java:53:21:53:33 | getClientIP(...) : String | UseOfLessTrustedSource.java:54:13:54:51 | !... | +| UseOfLessTrustedSource.java:74:27:74:62 | getHeader(...) : String | UseOfLessTrustedSource.java:78:16:78:37 | ...[...] : String | +| UseOfLessTrustedSource.java:78:16:78:37 | ...[...] : String | UseOfLessTrustedSource.java:53:21:53:33 | getClientIP(...) : String | nodes -| UseOfLessTrustedSource.java:19:26:19:61 | getHeader(...) : String | semmle.label | getHeader(...) : String | -| UseOfLessTrustedSource.java:25:16:25:25 | remoteAddr | semmle.label | remoteAddr | -| UseOfLessTrustedSource.java:30:21:30:56 | getHeader(...) : String | semmle.label | getHeader(...) : String | -| UseOfLessTrustedSource.java:32:60:32:61 | ip | semmle.label | ip | -| UseOfLessTrustedSource.java:58:28:58:48 | ... + ... | semmle.label | ... + ... | +| UseOfLessTrustedSource.java:20:21:20:56 | getHeader(...) : String | semmle.label | getHeader(...) : String | +| UseOfLessTrustedSource.java:22:60:22:61 | ip | semmle.label | ip | +| UseOfLessTrustedSource.java:25:18:25:53 | getHeader(...) : String | semmle.label | getHeader(...) : String | +| UseOfLessTrustedSource.java:26:64:26:65 | ip | semmle.label | ip | +| UseOfLessTrustedSource.java:29:18:29:56 | getHeader(...) : String | semmle.label | getHeader(...) : String | +| UseOfLessTrustedSource.java:30:67:30:68 | ip | semmle.label | ip | +| UseOfLessTrustedSource.java:33:18:33:52 | getHeader(...) : String | semmle.label | getHeader(...) : String | +| UseOfLessTrustedSource.java:34:63:34:64 | ip | semmle.label | ip | +| UseOfLessTrustedSource.java:37:18:37:58 | getHeader(...) : String | semmle.label | getHeader(...) : String | +| UseOfLessTrustedSource.java:38:69:38:70 | ip | semmle.label | ip | +| UseOfLessTrustedSource.java:41:18:41:47 | getHeader(...) : String | semmle.label | getHeader(...) : String | +| UseOfLessTrustedSource.java:42:58:42:59 | ip | semmle.label | ip | +| UseOfLessTrustedSource.java:48:28:48:48 | ... + ... | semmle.label | ... + ... | +| UseOfLessTrustedSource.java:53:21:53:33 | getClientIP(...) : String | semmle.label | getClientIP(...) : String | +| UseOfLessTrustedSource.java:54:13:54:51 | !... | semmle.label | !... | +| UseOfLessTrustedSource.java:74:27:74:62 | getHeader(...) : String | semmle.label | getHeader(...) : String | +| UseOfLessTrustedSource.java:78:16:78:37 | ...[...] : String | semmle.label | ...[...] : String | #select -| 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 | -| 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 | -| 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 | +| UseOfLessTrustedSource.java:22:60:22:61 | ip | UseOfLessTrustedSource.java:20:21:20:56 | getHeader(...) : String | UseOfLessTrustedSource.java:22:60:22:61 | ip | IP address spoofing might include code from $@. | UseOfLessTrustedSource.java:20:21:20:56 | getHeader(...) | this user input | +| UseOfLessTrustedSource.java:26:64:26:65 | ip | UseOfLessTrustedSource.java:25:18:25:53 | getHeader(...) : String | UseOfLessTrustedSource.java:26:64:26:65 | ip | IP address spoofing might include code from $@. | UseOfLessTrustedSource.java:25:18:25:53 | getHeader(...) | this user input | +| UseOfLessTrustedSource.java:30:67:30:68 | ip | UseOfLessTrustedSource.java:29:18:29:56 | getHeader(...) : String | UseOfLessTrustedSource.java:30:67:30:68 | ip | IP address spoofing might include code from $@. | UseOfLessTrustedSource.java:29:18:29:56 | getHeader(...) | this user input | +| UseOfLessTrustedSource.java:34:63:34:64 | ip | UseOfLessTrustedSource.java:33:18:33:52 | getHeader(...) : String | UseOfLessTrustedSource.java:34:63:34:64 | ip | IP address spoofing might include code from $@. | UseOfLessTrustedSource.java:33:18:33:52 | getHeader(...) | this user input | +| UseOfLessTrustedSource.java:38:69:38:70 | ip | UseOfLessTrustedSource.java:37:18:37:58 | getHeader(...) : String | UseOfLessTrustedSource.java:38:69:38:70 | ip | IP address spoofing might include code from $@. | UseOfLessTrustedSource.java:37:18:37:58 | getHeader(...) | this user input | +| UseOfLessTrustedSource.java:42:58:42:59 | ip | UseOfLessTrustedSource.java:41:18:41:47 | getHeader(...) : String | UseOfLessTrustedSource.java:42:58:42:59 | ip | IP address spoofing might include code from $@. | UseOfLessTrustedSource.java:41:18:41:47 | getHeader(...) | this user input | +| UseOfLessTrustedSource.java:48:28:48:48 | ... + ... | UseOfLessTrustedSource.java:20:21:20:56 | getHeader(...) : String | UseOfLessTrustedSource.java:48:28:48:48 | ... + ... | IP address spoofing might include code from $@. | UseOfLessTrustedSource.java:20:21:20:56 | getHeader(...) | this user input | +| UseOfLessTrustedSource.java:48:28:48:48 | ... + ... | UseOfLessTrustedSource.java:25:18:25:53 | getHeader(...) : String | UseOfLessTrustedSource.java:48:28:48:48 | ... + ... | IP address spoofing might include code from $@. | UseOfLessTrustedSource.java:25:18:25:53 | getHeader(...) | this user input | +| UseOfLessTrustedSource.java:48:28:48:48 | ... + ... | UseOfLessTrustedSource.java:29:18:29:56 | getHeader(...) : String | UseOfLessTrustedSource.java:48:28:48:48 | ... + ... | IP address spoofing might include code from $@. | UseOfLessTrustedSource.java:29:18:29:56 | getHeader(...) | this user input | +| UseOfLessTrustedSource.java:48:28:48:48 | ... + ... | UseOfLessTrustedSource.java:33:18:33:52 | getHeader(...) : String | UseOfLessTrustedSource.java:48:28:48:48 | ... + ... | IP address spoofing might include code from $@. | UseOfLessTrustedSource.java:33:18:33:52 | getHeader(...) | this user input | +| UseOfLessTrustedSource.java:48:28:48:48 | ... + ... | UseOfLessTrustedSource.java:37:18:37:58 | getHeader(...) : String | UseOfLessTrustedSource.java:48:28:48:48 | ... + ... | IP address spoofing might include code from $@. | UseOfLessTrustedSource.java:37:18:37:58 | getHeader(...) | this user input | +| UseOfLessTrustedSource.java:48:28:48:48 | ... + ... | UseOfLessTrustedSource.java:41:18:41:47 | getHeader(...) : String | UseOfLessTrustedSource.java:48:28:48:48 | ... + ... | IP address spoofing might include code from $@. | UseOfLessTrustedSource.java:41:18:41:47 | getHeader(...) | this user input | +| UseOfLessTrustedSource.java:54:13:54:51 | !... | UseOfLessTrustedSource.java:74:27:74:62 | getHeader(...) : String | UseOfLessTrustedSource.java:54:13:54:51 | !... | IP address spoofing might include code from $@. | UseOfLessTrustedSource.java:74:27:74:62 | getHeader(...) | this user input | diff --git a/java/ql/test/experimental/query-tests/security/CWE-348/UseOfLessTrustedSource.java b/java/ql/test/experimental/query-tests/security/CWE-348/UseOfLessTrustedSource.java index d338134ed3e..22d7100223c 100644 --- a/java/ql/test/experimental/query-tests/security/CWE-348/UseOfLessTrustedSource.java +++ b/java/ql/test/experimental/query-tests/security/CWE-348/UseOfLessTrustedSource.java @@ -2,6 +2,7 @@ import javax.servlet.http.HttpServletRequest; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.ResponseBody; @@ -11,24 +12,13 @@ public class UseOfLessTrustedSource { private static final Logger log = LoggerFactory.getLogger(UseOfLessTrustedSource.class); - @GetMapping(value = "bad1") - @ResponseBody - public String bad1(HttpServletRequest request) { - String remoteAddr = ""; - if (request != null) { - remoteAddr = request.getHeader("X-FORWARDED-FOR"); - remoteAddr = remoteAddr.split(",")[0]; - if (remoteAddr == null || "".equals(remoteAddr)) { - remoteAddr = request.getRemoteAddr(); - } - } - return remoteAddr; - } + @Autowired + private HttpServletRequest request; - @GetMapping(value = "bad2") - public void bad2(HttpServletRequest request) { + @GetMapping(value = "bad1") + public void bad1(HttpServletRequest request) { String ip = request.getHeader("X-Forwarded-For"); - + log.debug("getClientIP header X-Forwarded-For:{}", ip); if (StringUtils.isBlank(ip) || StringUtils.equalsIgnoreCase("unknown", ip)) { @@ -58,6 +48,14 @@ public class UseOfLessTrustedSource { System.out.println("client ip is: " + ip); } + @GetMapping(value = "bad2") + public void bad2(HttpServletRequest request) { + String ip = getClientIP(); + if (!StringUtils.startsWith(ip, "192.168.")) { + new Exception("ip illegal"); + } + } + @GetMapping(value = "good1") @ResponseBody public String good1(HttpServletRequest request) { @@ -71,4 +69,12 @@ public class UseOfLessTrustedSource { } return remoteAddr; } + + protected String getClientIP() { + String xfHeader = request.getHeader("X-Forwarded-For"); + if (xfHeader == null) { + return request.getRemoteAddr(); + } + return xfHeader.split(",")[0]; + } } \ No newline at end of file diff --git a/java/ql/test/experimental/query-tests/security/CWE-348/options b/java/ql/test/experimental/query-tests/security/CWE-348/options index 40bcee8c133..7edf075ba8e 100644 --- a/java/ql/test/experimental/query-tests/security/CWE-348/options +++ b/java/ql/test/experimental/query-tests/security/CWE-348/options @@ -1,2 +1 @@ -//semmle-extractor-options: --javac-args -cp ${testdir}/../../../../stubs/servlet-api-2.4:${testdir}/../../../../stubs/springframework-5.2.3/:${tes -tdir}/../../../../stubs/slf4j-api-1.6.4/:${testdir}/../../../../stubs/apache-commons-lang3-3.7/ \ No newline at end of file +//semmle-extractor-options: --javac-args -cp ${testdir}/../../../../stubs/servlet-api-2.4:${testdir}/../../../../stubs/springframework-5.2.3/:${testdir}/../../../../stubs/slf4j-api-1.6.4/:${testdir}/../../../../stubs/apache-commons-lang3-3.7/ \ No newline at end of file diff --git a/java/ql/test/stubs/springframework-5.2.3/org/springframework/beans/factory/annotation/Autowired.java b/java/ql/test/stubs/springframework-5.2.3/org/springframework/beans/factory/annotation/Autowired.java new file mode 100644 index 00000000000..8fcef979360 --- /dev/null +++ b/java/ql/test/stubs/springframework-5.2.3/org/springframework/beans/factory/annotation/Autowired.java @@ -0,0 +1,14 @@ +package org.springframework.beans.factory.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface Autowired { + boolean required() default true; +} \ No newline at end of file