mirror of
https://github.com/github/codeql.git
synced 2025-12-22 11:46:32 +01:00
Add path check in a security context (redirect)
This commit is contained in:
@@ -12,20 +12,37 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import java
|
import java
|
||||||
|
import semmle.code.java.controlflow.Guards
|
||||||
import semmle.code.java.dataflow.ExternalFlow
|
import semmle.code.java.dataflow.ExternalFlow
|
||||||
import semmle.code.java.dataflow.FlowSources
|
import semmle.code.java.dataflow.FlowSources
|
||||||
|
import semmle.code.java.security.UrlRedirect
|
||||||
import DataFlow::PathGraph
|
import DataFlow::PathGraph
|
||||||
import Regex
|
import Regex
|
||||||
|
|
||||||
|
/** Source model of remote flow source with servlets. */
|
||||||
|
private class GetServletUriSource extends SourceModelCsv {
|
||||||
|
override predicate row(string row) {
|
||||||
|
row =
|
||||||
|
[
|
||||||
|
"javax.servlet.http;HttpServletRequest;false;getPathInfo;();;ReturnValue;uri-path;manual",
|
||||||
|
"javax.servlet.http;HttpServletRequest;false;getPathTranslated;();;ReturnValue;uri-path;manual",
|
||||||
|
"javax.servlet.http;HttpServletRequest;false;getRequestURI;();;ReturnValue;uri-path;manual",
|
||||||
|
"javax.servlet.http;HttpServletRequest;false;getRequestURL;();;ReturnValue;uri-path;manual",
|
||||||
|
"javax.servlet.http;HttpServletRequest;false;getServletPath;();;ReturnValue;uri-path;manual"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* `.` without a `\` prefix, which is likely not a character literal in regex
|
* `.` without a `\` prefix, which is likely not a character literal in regex
|
||||||
*/
|
*/
|
||||||
class PermissiveDotStr extends StringLiteral {
|
class PermissiveDotStr extends StringLiteral {
|
||||||
PermissiveDotStr() {
|
PermissiveDotStr() {
|
||||||
// Find `.` in a string that is not prefixed with `\`
|
// Find `.` in a string that is not prefixed with `\` and ends with `.*` (no suffix like file extension)
|
||||||
exists(string s, int i | this.getValue() = s |
|
exists(string s, int i | this.getValue() = s |
|
||||||
s.indexOf(".") = i and
|
s.indexOf(".*") = i and
|
||||||
not s.charAt(i - 1) = "\\"
|
not s.charAt(i - 1) = "\\" and
|
||||||
|
s.length() = i + 2
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -86,9 +103,26 @@ class PermissiveDotRegexConfig extends DataFlow::Configuration {
|
|||||||
class MatchRegexConfiguration extends TaintTracking::Configuration {
|
class MatchRegexConfiguration extends TaintTracking::Configuration {
|
||||||
MatchRegexConfiguration() { this = "PermissiveDotRegex::MatchRegexConfiguration" }
|
MatchRegexConfiguration() { this = "PermissiveDotRegex::MatchRegexConfiguration" }
|
||||||
|
|
||||||
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
|
override predicate isSource(DataFlow::Node source) { sourceNode(source, "uri-path") }
|
||||||
|
|
||||||
override predicate isSink(DataFlow::Node sink) { sink instanceof MatchRegexSink }
|
override predicate isSink(DataFlow::Node sink) {
|
||||||
|
sink instanceof MatchRegexSink and
|
||||||
|
exists(
|
||||||
|
Guard guard, Expr se, Expr ce // used in a condition to control url redirect, which is a typical security enforcement
|
||||||
|
|
|
||||||
|
(
|
||||||
|
sink.asExpr() = ce.(MethodAccess).getQualifier() or
|
||||||
|
sink.asExpr() = ce.(MethodAccess).getAnArgument() or
|
||||||
|
sink.asExpr() = ce
|
||||||
|
) and
|
||||||
|
(
|
||||||
|
DataFlow::localExprFlow(ce, guard.(MethodAccess).getQualifier()) or
|
||||||
|
DataFlow::localExprFlow(ce, guard.(MethodAccess).getAnArgument())
|
||||||
|
) and
|
||||||
|
DataFlow::exprNode(se) instanceof UrlRedirectSink and
|
||||||
|
guard.controls(se.getBasicBlock(), true)
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
from
|
from
|
||||||
|
|||||||
@@ -95,4 +95,3 @@ class PatternMatcherRegexSink extends MatchRegexSink {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,10 +22,14 @@ public class DotRegexServlet extends HttpServlet {
|
|||||||
|
|
||||||
if (m.matches()) {
|
if (m.matches()) {
|
||||||
// Protected page - check access token and redirect to login page
|
// Protected page - check access token and redirect to login page
|
||||||
|
if (!request.getSession().getAttribute("secAttr").equals("secValue")) {
|
||||||
|
response.sendRedirect("/login.html");
|
||||||
|
return;
|
||||||
} else {
|
} else {
|
||||||
// Not protected page - render content
|
// Not protected page - render content
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// GOOD: A string with line return e.g. `/protected/%0dxyz` cannot bypass the path check
|
// GOOD: A string with line return e.g. `/protected/%0dxyz` cannot bypass the path check
|
||||||
protected void doGet2(HttpServletRequest request, HttpServletResponse response)
|
protected void doGet2(HttpServletRequest request, HttpServletResponse response)
|
||||||
@@ -37,24 +41,32 @@ public class DotRegexServlet extends HttpServlet {
|
|||||||
|
|
||||||
if (m.matches()) {
|
if (m.matches()) {
|
||||||
// Protected page - check access token and redirect to login page
|
// Protected page - check access token and redirect to login page
|
||||||
|
if (!request.getSession().getAttribute("secAttr").equals("secValue")) {
|
||||||
|
response.sendRedirect("/login.html");
|
||||||
|
return;
|
||||||
} else {
|
} else {
|
||||||
// Not protected page - render content
|
// Not protected page - render content
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// BAD: A string with line return e.g. `/protected/%0axyz` can bypass the path check
|
// BAD: A string with line return e.g. `/protected/%0axyz` can bypass the path check
|
||||||
protected void doGet3(HttpServletRequest request, HttpServletResponse response)
|
protected void doGet3(HttpServletRequest request, HttpServletResponse response)
|
||||||
throws ServletException, IOException {
|
throws ServletException, IOException {
|
||||||
String source = request.getPathInfo();
|
String source = request.getRequestURI();
|
||||||
|
|
||||||
boolean matches = source.matches(PROTECTED_PATTERN);
|
boolean matches = source.matches(PROTECTED_PATTERN);
|
||||||
|
|
||||||
if (matches) {
|
if (matches) {
|
||||||
// Protected page - check access token and redirect to login page
|
// Protected page - check access token and redirect to login page
|
||||||
|
if (!request.getSession().getAttribute("secAttr").equals("secValue")) {
|
||||||
|
response.sendRedirect("/login.html");
|
||||||
|
return;
|
||||||
} else {
|
} else {
|
||||||
// Not protected page - render content
|
// Not protected page - render content
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// BAD: A string with line return e.g. `/protected/%0axyz` can bypass the path check
|
// BAD: A string with line return e.g. `/protected/%0axyz` can bypass the path check
|
||||||
protected void doGet4(HttpServletRequest request, HttpServletResponse response)
|
protected void doGet4(HttpServletRequest request, HttpServletResponse response)
|
||||||
@@ -65,10 +77,14 @@ public class DotRegexServlet extends HttpServlet {
|
|||||||
|
|
||||||
if (matches) {
|
if (matches) {
|
||||||
// Protected page - check access token and redirect to login page
|
// Protected page - check access token and redirect to login page
|
||||||
|
if (!request.getSession().getAttribute("secAttr").equals("secValue")) {
|
||||||
|
response.sendRedirect("/login.html");
|
||||||
|
return;
|
||||||
} else {
|
} else {
|
||||||
// Not protected page - render content
|
// Not protected page - render content
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// GOOD: Only a specific path can pass the validation
|
// GOOD: Only a specific path can pass the validation
|
||||||
protected void doGet5(HttpServletRequest request, HttpServletResponse response)
|
protected void doGet5(HttpServletRequest request, HttpServletResponse response)
|
||||||
@@ -80,8 +96,12 @@ public class DotRegexServlet extends HttpServlet {
|
|||||||
|
|
||||||
if (m.matches()) {
|
if (m.matches()) {
|
||||||
// Protected page - check access token and redirect to login page
|
// Protected page - check access token and redirect to login page
|
||||||
|
if (!request.getSession().getAttribute("secAttr").equals("secValue")) {
|
||||||
|
response.sendRedirect("/login.html");
|
||||||
|
return;
|
||||||
} else {
|
} else {
|
||||||
// Not protected page - render content
|
// Not protected page - render content
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,31 +1,31 @@
|
|||||||
edges
|
edges
|
||||||
| DotRegexServlet.java:11:30:11:46 | PROTECTED_PATTERN : String | DotRegexServlet.java:20:31:20:47 | PROTECTED_PATTERN |
|
| DotRegexServlet.java:11:30:11:46 | PROTECTED_PATTERN : String | DotRegexServlet.java:20:31:20:47 | PROTECTED_PATTERN |
|
||||||
| DotRegexServlet.java:11:30:11:46 | PROTECTED_PATTERN : String | DotRegexServlet.java:50:36:50:52 | PROTECTED_PATTERN |
|
| DotRegexServlet.java:11:30:11:46 | PROTECTED_PATTERN : String | DotRegexServlet.java:58:36:58:52 | PROTECTED_PATTERN |
|
||||||
| DotRegexServlet.java:11:30:11:46 | PROTECTED_PATTERN : String | DotRegexServlet.java:64:37:64:53 | PROTECTED_PATTERN |
|
| DotRegexServlet.java:11:30:11:46 | PROTECTED_PATTERN : String | DotRegexServlet.java:76:37:76:53 | PROTECTED_PATTERN |
|
||||||
| DotRegexServlet.java:11:50:11:64 | "/protected/.*" : String | DotRegexServlet.java:11:30:11:46 | PROTECTED_PATTERN : String |
|
| DotRegexServlet.java:11:50:11:64 | "/protected/.*" : String | DotRegexServlet.java:11:30:11:46 | PROTECTED_PATTERN : String |
|
||||||
| DotRegexServlet.java:18:19:18:39 | getPathInfo(...) : String | DotRegexServlet.java:21:25:21:30 | source |
|
| DotRegexServlet.java:18:19:18:39 | getPathInfo(...) : String | DotRegexServlet.java:21:25:21:30 | source |
|
||||||
| DotRegexServlet.java:33:19:33:39 | getPathInfo(...) : String | DotRegexServlet.java:36:25:36:30 | source |
|
| DotRegexServlet.java:37:19:37:39 | getPathInfo(...) : String | DotRegexServlet.java:40:25:40:30 | source |
|
||||||
| DotRegexServlet.java:48:19:48:39 | getPathInfo(...) : String | DotRegexServlet.java:50:21:50:26 | source |
|
| DotRegexServlet.java:56:19:56:41 | getRequestURI(...) : String | DotRegexServlet.java:58:21:58:26 | source |
|
||||||
| DotRegexServlet.java:62:19:62:39 | getPathInfo(...) : String | DotRegexServlet.java:64:56:64:61 | source |
|
| DotRegexServlet.java:74:19:74:39 | getPathInfo(...) : String | DotRegexServlet.java:76:56:76:61 | source |
|
||||||
| DotRegexServlet.java:76:19:76:39 | getPathInfo(...) : String | DotRegexServlet.java:79:25:79:30 | source |
|
| DotRegexServlet.java:92:19:92:39 | getPathInfo(...) : String | DotRegexServlet.java:95:25:95:30 | source |
|
||||||
nodes
|
nodes
|
||||||
| DotRegexServlet.java:11:30:11:46 | PROTECTED_PATTERN : String | semmle.label | PROTECTED_PATTERN : String |
|
| DotRegexServlet.java:11:30:11:46 | PROTECTED_PATTERN : String | semmle.label | PROTECTED_PATTERN : String |
|
||||||
| DotRegexServlet.java:11:50:11:64 | "/protected/.*" : String | semmle.label | "/protected/.*" : String |
|
| DotRegexServlet.java:11:50:11:64 | "/protected/.*" : String | semmle.label | "/protected/.*" : String |
|
||||||
| DotRegexServlet.java:18:19:18:39 | getPathInfo(...) : String | semmle.label | getPathInfo(...) : String |
|
| DotRegexServlet.java:18:19:18:39 | getPathInfo(...) : String | semmle.label | getPathInfo(...) : String |
|
||||||
| DotRegexServlet.java:20:31:20:47 | PROTECTED_PATTERN | semmle.label | PROTECTED_PATTERN |
|
| DotRegexServlet.java:20:31:20:47 | PROTECTED_PATTERN | semmle.label | PROTECTED_PATTERN |
|
||||||
| DotRegexServlet.java:21:25:21:30 | source | semmle.label | source |
|
| DotRegexServlet.java:21:25:21:30 | source | semmle.label | source |
|
||||||
| DotRegexServlet.java:33:19:33:39 | getPathInfo(...) : String | semmle.label | getPathInfo(...) : String |
|
| DotRegexServlet.java:37:19:37:39 | getPathInfo(...) : String | semmle.label | getPathInfo(...) : String |
|
||||||
| DotRegexServlet.java:36:25:36:30 | source | semmle.label | source |
|
| DotRegexServlet.java:40:25:40:30 | source | semmle.label | source |
|
||||||
| DotRegexServlet.java:48:19:48:39 | getPathInfo(...) : String | semmle.label | getPathInfo(...) : String |
|
| DotRegexServlet.java:56:19:56:41 | getRequestURI(...) : String | semmle.label | getRequestURI(...) : String |
|
||||||
| DotRegexServlet.java:50:21:50:26 | source | semmle.label | source |
|
| DotRegexServlet.java:58:21:58:26 | source | semmle.label | source |
|
||||||
| DotRegexServlet.java:50:36:50:52 | PROTECTED_PATTERN | semmle.label | PROTECTED_PATTERN |
|
| DotRegexServlet.java:58:36:58:52 | PROTECTED_PATTERN | semmle.label | PROTECTED_PATTERN |
|
||||||
| DotRegexServlet.java:62:19:62:39 | getPathInfo(...) : String | semmle.label | getPathInfo(...) : String |
|
| DotRegexServlet.java:74:19:74:39 | getPathInfo(...) : String | semmle.label | getPathInfo(...) : String |
|
||||||
| DotRegexServlet.java:64:37:64:53 | PROTECTED_PATTERN | semmle.label | PROTECTED_PATTERN |
|
| DotRegexServlet.java:76:37:76:53 | PROTECTED_PATTERN | semmle.label | PROTECTED_PATTERN |
|
||||||
| DotRegexServlet.java:64:56:64:61 | source | semmle.label | source |
|
| DotRegexServlet.java:76:56:76:61 | source | semmle.label | source |
|
||||||
| DotRegexServlet.java:76:19:76:39 | getPathInfo(...) : String | semmle.label | getPathInfo(...) : String |
|
| DotRegexServlet.java:92:19:92:39 | getPathInfo(...) : String | semmle.label | getPathInfo(...) : String |
|
||||||
| DotRegexServlet.java:79:25:79:30 | source | semmle.label | source |
|
| DotRegexServlet.java:95:25:95:30 | source | semmle.label | source |
|
||||||
subpaths
|
subpaths
|
||||||
#select
|
#select
|
||||||
| DotRegexServlet.java:21:25:21:30 | source | DotRegexServlet.java:18:19:18:39 | getPathInfo(...) : String | DotRegexServlet.java:21:25:21:30 | source | Potentially authentication bypass due to $@. | DotRegexServlet.java:18:19:18:39 | getPathInfo(...) | user-provided value |
|
| DotRegexServlet.java:21:25:21:30 | source | DotRegexServlet.java:18:19:18:39 | getPathInfo(...) : String | DotRegexServlet.java:21:25:21:30 | source | Potentially authentication bypass due to $@. | DotRegexServlet.java:18:19:18:39 | getPathInfo(...) | user-provided value |
|
||||||
| DotRegexServlet.java:50:21:50:26 | source | DotRegexServlet.java:48:19:48:39 | getPathInfo(...) : String | DotRegexServlet.java:50:21:50:26 | source | Potentially authentication bypass due to $@. | DotRegexServlet.java:48:19:48:39 | getPathInfo(...) | user-provided value |
|
| DotRegexServlet.java:58:21:58:26 | source | DotRegexServlet.java:56:19:56:41 | getRequestURI(...) : String | DotRegexServlet.java:58:21:58:26 | source | Potentially authentication bypass due to $@. | DotRegexServlet.java:56:19:56:41 | getRequestURI(...) | user-provided value |
|
||||||
| DotRegexServlet.java:64:56:64:61 | source | DotRegexServlet.java:62:19:62:39 | getPathInfo(...) : String | DotRegexServlet.java:64:56:64:61 | source | Potentially authentication bypass due to $@. | DotRegexServlet.java:62:19:62:39 | getPathInfo(...) | user-provided value |
|
| DotRegexServlet.java:76:56:76:61 | source | DotRegexServlet.java:74:19:74:39 | getPathInfo(...) : String | DotRegexServlet.java:76:56:76:61 | source | Potentially authentication bypass due to $@. | DotRegexServlet.java:74:19:74:39 | getPathInfo(...) | user-provided value |
|
||||||
|
|||||||
Reference in New Issue
Block a user