mirror of
https://github.com/github/codeql.git
synced 2025-12-22 03:36:30 +01:00
Promote SSRF query to main query set
This commit is contained in:
20
java/ql/src/Security/CWE/CWE-918/RequestForgery.java
Normal file
20
java/ql/src/Security/CWE/CWE-918/RequestForgery.java
Normal file
@@ -0,0 +1,20 @@
|
||||
import java.net.http.HttpClient;
|
||||
|
||||
public class SSRF extends HttpServlet {
|
||||
private static final String VALID_URI = "http://lgtm.com";
|
||||
private HttpClient client = HttpClient.newHttpClient();
|
||||
|
||||
protected void doGet(HttpServletRequest request, HttpServletResponse response)
|
||||
throws ServletException, IOException {
|
||||
URI uri = new URI(request.getParameter("uri"));
|
||||
// BAD: a request parameter is incorporated without validation into a Http request
|
||||
HttpRequest r = HttpRequest.newBuilder(uri).build();
|
||||
client.send(r, null);
|
||||
|
||||
// GOOD: the request parameter is validated against a known fixed string
|
||||
if (VALID_URI.equals(request.getParameter("uri"))) {
|
||||
HttpRequest r2 = HttpRequest.newBuilder(uri).build();
|
||||
client.send(r2, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
37
java/ql/src/Security/CWE/CWE-918/RequestForgery.qhelp
Normal file
37
java/ql/src/Security/CWE/CWE-918/RequestForgery.qhelp
Normal file
@@ -0,0 +1,37 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
|
||||
<overview>
|
||||
<p>Directly incorporating user input into a HTTP request without validating the input
|
||||
can facilitate Server Side Request Forgery (SSRF) attacks. In these attacks, the server
|
||||
may be tricked into making a request and interacting with an attacker-controlled server.
|
||||
</p>
|
||||
|
||||
</overview>
|
||||
<recommendation>
|
||||
|
||||
<p>To guard against SSRF attacks, it is advisable to avoid putting user input
|
||||
directly into the request URL. Instead, maintain a list of authorized
|
||||
URLs on the server; then choose from that list based on the user input provided.</p>
|
||||
|
||||
</recommendation>
|
||||
<example>
|
||||
|
||||
<p>The following example shows an HTTP request parameter being used directly in a forming a
|
||||
new request without validating the input, which facilitates SSRF attacks.
|
||||
It also shows how to remedy the problem by validating the user input against a known fixed string.
|
||||
</p>
|
||||
|
||||
<sample src="RequestForgery.java" />
|
||||
|
||||
</example>
|
||||
<references>
|
||||
<li>
|
||||
<a href="https://owasp.org/www-community/attacks/Server_Side_Request_Forgery">OWASP SSRF</a>
|
||||
</li>
|
||||
|
||||
</references>
|
||||
</qhelp>
|
||||
33
java/ql/src/Security/CWE/CWE-918/RequestForgery.ql
Normal file
33
java/ql/src/Security/CWE/CWE-918/RequestForgery.ql
Normal file
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* @name Server Side Request Forgery (SSRF)
|
||||
* @description Making web requests based on unvalidated user-input
|
||||
* may cause server to communicate with malicious servers.
|
||||
* @kind path-problem
|
||||
* @problem.severity error
|
||||
* @precision high
|
||||
* @id java/ssrf
|
||||
* @tags security
|
||||
* external/cwe/cwe-918
|
||||
*/
|
||||
|
||||
import java
|
||||
import semmle.code.java.dataflow.FlowSources
|
||||
import RequestForgery
|
||||
import DataFlow::PathGraph
|
||||
|
||||
class RequestForgeryConfiguration extends TaintTracking::Configuration {
|
||||
RequestForgeryConfiguration() { this = "Server Side Request Forgery" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof RequestForgerySink }
|
||||
|
||||
override predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
requestForgeryStep(pred, succ)
|
||||
}
|
||||
}
|
||||
|
||||
from DataFlow::PathNode source, DataFlow::PathNode sink, RequestForgeryConfiguration conf
|
||||
where conf.hasFlowPath(source, sink)
|
||||
select sink.getNode(), source, sink, "Potential server side request forgery due to $@.",
|
||||
source.getNode(), "a user-provided value"
|
||||
192
java/ql/src/Security/CWE/CWE-918/RequestForgery.qll
Normal file
192
java/ql/src/Security/CWE/CWE-918/RequestForgery.qll
Normal file
@@ -0,0 +1,192 @@
|
||||
import java
|
||||
import semmle.code.java.frameworks.Networking
|
||||
import semmle.code.java.frameworks.ApacheHttp
|
||||
import semmle.code.java.frameworks.spring.Spring
|
||||
import semmle.code.java.frameworks.JaxWS
|
||||
import semmle.code.java.frameworks.javase.Http
|
||||
import semmle.code.java.dataflow.DataFlow
|
||||
|
||||
predicate requestForgeryStep(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
// propagate to a URI when its host is assigned to
|
||||
exists(UriCreation c | c.getHostArg() = pred.asExpr() | succ.asExpr() = c)
|
||||
or
|
||||
// propagate to a URL when its host is assigned to
|
||||
exists(UrlConstructorCall c | c.getHostArg() = pred.asExpr() | succ.asExpr() = c)
|
||||
or
|
||||
// propagate to a RequestEntity when its url is assigned to
|
||||
exists(MethodAccess m |
|
||||
m.getMethod().getDeclaringType() instanceof SpringRequestEntity and
|
||||
(
|
||||
m.getMethod().hasName(["get", "post", "head", "delete", "options", "patch", "put"]) and
|
||||
m.getArgument(0) = pred.asExpr() and
|
||||
m = succ.asExpr()
|
||||
or
|
||||
m.getMethod().hasName("method") and
|
||||
m.getArgument(1) = pred.asExpr() and
|
||||
m = succ.asExpr()
|
||||
)
|
||||
)
|
||||
or
|
||||
// propagate from a `RequestEntity<>$BodyBuilder` to a `RequestEntity`
|
||||
// when the builder is tainted
|
||||
exists(MethodAccess m, RefType t |
|
||||
m.getMethod().getDeclaringType() = t and
|
||||
t.hasQualifiedName("org.springframework.http", "RequestEntity<>$BodyBuilder") and
|
||||
m.getMethod().hasName("body") and
|
||||
m.getQualifier() = pred.asExpr() and
|
||||
m = succ.asExpr()
|
||||
)
|
||||
}
|
||||
|
||||
/** A data flow sink for request forgery vulnerabilities. */
|
||||
abstract class RequestForgerySink extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* An argument to an url `openConnection` or `openStream` call
|
||||
* taken as a sink for request forgery vulnerabilities.
|
||||
*/
|
||||
private class UrlOpen extends RequestForgerySink {
|
||||
UrlOpen() {
|
||||
exists(MethodAccess ma |
|
||||
ma.getMethod() instanceof UrlOpenConnectionMethod or
|
||||
ma.getMethod() instanceof UrlOpenStreamMethod
|
||||
|
|
||||
this.asExpr() = ma.getQualifier()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An argument to an Apache `setURI` call taken as a
|
||||
* sink for request forgery vulnerabilities.
|
||||
*/
|
||||
private class ApacheSetUri extends RequestForgerySink {
|
||||
ApacheSetUri() {
|
||||
exists(MethodAccess ma |
|
||||
ma.getReceiverType() instanceof ApacheHttpRequest and
|
||||
ma.getMethod().hasName("setURI")
|
||||
|
|
||||
this.asExpr() = ma.getArgument(0)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An argument to any Apache Request Instantiation call taken as a
|
||||
* sink for request forgery vulnerabilities.
|
||||
*/
|
||||
private class ApacheHttpRequestInstantiation extends RequestForgerySink {
|
||||
ApacheHttpRequestInstantiation() {
|
||||
exists(ClassInstanceExpr c | c.getConstructedType() instanceof ApacheHttpRequest |
|
||||
this.asExpr() = c.getArgument(0)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An argument to a Apache RequestBuilder method call taken as a
|
||||
* sink for request forgery vulnerabilities.
|
||||
*/
|
||||
private class ApacheHttpRequestBuilderArgument extends RequestForgerySink {
|
||||
ApacheHttpRequestBuilderArgument() {
|
||||
exists(MethodAccess ma |
|
||||
ma.getReceiverType() instanceof TypeApacheHttpRequestBuilder and
|
||||
ma.getMethod().hasName(["setURI", "get", "post", "put", "optons", "head", "delete"])
|
||||
|
|
||||
this.asExpr() = ma.getArgument(0)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An argument to any Java.net.http.request Instantiation call taken as a
|
||||
* sink for request forgery vulnerabilities.
|
||||
*/
|
||||
private class HttpRequestNewBuilder extends RequestForgerySink {
|
||||
HttpRequestNewBuilder() {
|
||||
exists(MethodAccess call |
|
||||
call.getCallee().hasName("newBuilder") and
|
||||
call.getMethod().getDeclaringType().getName() = "HttpRequest"
|
||||
|
|
||||
this.asExpr() = call.getArgument(0)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An argument to an Http Builder `uri` call taken as a
|
||||
* sink for request forgery vulnerabilities.
|
||||
*/
|
||||
private class HttpBuilderUriArgument extends RequestForgerySink {
|
||||
HttpBuilderUriArgument() {
|
||||
exists(MethodAccess ma | ma.getMethod() instanceof HttpBuilderUri |
|
||||
this.asExpr() = ma.getArgument(0)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An argument to a Spring Rest Template method call taken as a
|
||||
* sink for request forgery vulnerabilities.
|
||||
*/
|
||||
private class SpringRestTemplateArgument extends RequestForgerySink {
|
||||
SpringRestTemplateArgument() {
|
||||
exists(MethodAccess ma |
|
||||
this.asExpr() = ma.getMethod().(SpringRestTemplateUrlMethods).getUrlArgument(ma)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An argument to `javax.ws.rs.Client`s `target` method call taken as a
|
||||
* sink for request forgery vulnerabilities.
|
||||
*/
|
||||
private class JaxRsClientTarget extends RequestForgerySink {
|
||||
JaxRsClientTarget() {
|
||||
exists(MethodAccess ma |
|
||||
ma.getMethod().getDeclaringType() instanceof JaxRsClient and
|
||||
ma.getMethod().hasName("target")
|
||||
|
|
||||
this.asExpr() = ma.getArgument(0)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An argument to `org.springframework.http.RequestEntity`s constructor call
|
||||
* which is an URI taken as a sink for request forgery vulnerabilities.
|
||||
*/
|
||||
private class RequestEntityUriArg extends RequestForgerySink {
|
||||
RequestEntityUriArg() {
|
||||
exists(ClassInstanceExpr e, Argument a |
|
||||
e.getConstructedType() instanceof SpringRequestEntity and
|
||||
e.getAnArgument() = a and
|
||||
a.getType() instanceof TypeUri and
|
||||
this.asExpr() = a
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A class representing all Spring Rest Template methods
|
||||
* which take an URL as an argument.
|
||||
*/
|
||||
private class SpringRestTemplateUrlMethods extends Method {
|
||||
SpringRestTemplateUrlMethods() {
|
||||
this.getDeclaringType() instanceof SpringRestTemplate and
|
||||
this.hasName([
|
||||
"doExecute", "postForEntity", "postForLocation", "postForObject", "put", "exchange",
|
||||
"execute", "getForEntity", "getForObject", "patchForObject"
|
||||
])
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the argument which corresponds to a URL argument
|
||||
* passed as a `java.net.URL` object or as a string or the like
|
||||
*/
|
||||
Argument getUrlArgument(MethodAccess ma) {
|
||||
// doExecute(URI url, HttpMethod method, RequestCallback requestCallback,
|
||||
// ResponseExtractor<T> responseExtractor)
|
||||
result = ma.getArgument(0)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user