1
1
/**
2
2
* @name Sensitive cookies without the HttpOnly response header set
3
- * @description Sensitive cookies without 'HttpOnly' leaves session cookies vulnerable to an XSS attack.
3
+ * @description Sensitive cookies without the 'HttpOnly' flag set leaves session cookies vulnerable to
4
+ * an XSS attack. This query checks whether 'HttpOnly' is not set in a Java Cookie object
5
+ * or the 'Set-Cookie' HTTP header.
4
6
* @kind path-problem
5
7
* @id java/sensitive-cookie-not-httponly
6
8
* @tags security
@@ -25,14 +27,14 @@ string getCsrfCookieNameRegex() { result = "(?i).*(csrf).*" }
25
27
* they are special cookies implementing the Synchronizer Token Pattern that can be used in JavaScript.
26
28
*/
27
29
predicate isSensitiveCookieNameExpr ( Expr expr ) {
28
- exists ( string s | s = expr .( CompileTimeConstantExpr ) .getStringValue ( ) . toLowerCase ( ) |
30
+ exists ( string s | s = expr .( CompileTimeConstantExpr ) .getStringValue ( ) |
29
31
s .regexpMatch ( getSensitiveCookieNameRegex ( ) ) and not s .regexpMatch ( getCsrfCookieNameRegex ( ) )
30
32
)
31
33
or
32
34
isSensitiveCookieNameExpr ( expr .( AddExpr ) .getAnOperand ( ) )
33
35
}
34
36
35
- /** The method call `Set-Cookie` of `addHeader` or `setHeader` . */
37
+ /** A method call that sets a `Set-Cookie` header . */
36
38
class SetCookieMethodAccess extends MethodAccess {
37
39
SetCookieMethodAccess ( ) {
38
40
(
@@ -59,33 +61,31 @@ class MatchesHttpOnlyConfiguration extends TaintTracking2::Configuration {
59
61
}
60
62
}
61
63
62
- /** The cookie class of Java EE . */
64
+ /** A class descended from `javax.servlet.http.Cookie` or `javax/jakarta.ws.rs.core.Cookie` . */
63
65
class CookieClass extends RefType {
64
66
CookieClass ( ) {
65
67
this .getASupertype * ( )
66
68
.hasQualifiedName ( [ "javax.servlet.http" , "javax.ws.rs.core" , "jakarta.ws.rs.core" ] , "Cookie" )
67
69
}
68
70
}
69
71
70
- /** Holds if the `Expr` expr is evaluated to boolean true. */
71
- predicate isBooleanTrue ( Expr expr ) {
72
+ /** Holds if the `expr` is `true` or a variable that is ever assigned ` true` . */
73
+ predicate mayBeBooleanTrue ( Expr expr ) {
72
74
expr .( CompileTimeConstantExpr ) .getBooleanValue ( ) = true or
73
75
expr .( VarAccess ) .getVariable ( ) .getAnAssignedValue ( ) .( CompileTimeConstantExpr ) .getBooleanValue ( ) =
74
76
true
75
77
}
76
78
77
79
/** Holds if the method call sets the `HttpOnly` flag. */
78
- predicate setHttpOnlyInCookie ( MethodAccess ma ) {
80
+ predicate setsCookieHttpOnly ( MethodAccess ma ) {
79
81
ma .getMethod ( ) .getName ( ) = "setHttpOnly" and
80
82
(
81
- isBooleanTrue ( ma .getArgument ( 0 ) ) // boolean literal true
83
+ ma .getArgument ( 0 ) . ( CompileTimeConstantExpr ) . getBooleanValue ( ) = true
82
84
or
83
- exists (
84
- MethodAccess mpa , int i // runtime assignment of boolean value true
85
- |
86
- TaintTracking:: localTaint ( DataFlow:: parameterNode ( mpa .getMethod ( ) .getParameter ( i ) ) ,
87
- DataFlow:: exprNode ( ma .getArgument ( 0 ) ) ) and
88
- isBooleanTrue ( mpa .getArgument ( i ) )
85
+ // any use of setHttpOnly(x) where x isn't false is probably safe
86
+ not exists ( VarAccess va |
87
+ ma .getArgument ( 0 ) .( VarAccess ) .getVariable ( ) .getAnAccess ( ) = va and
88
+ va .getVariable ( ) .getAnAssignedValue ( ) .( CompileTimeConstantExpr ) .getBooleanValue ( ) = false
89
89
)
90
90
)
91
91
}
@@ -99,7 +99,7 @@ class SetHttpOnlyInCookieConfiguration extends TaintTracking2::Configuration {
99
99
100
100
override predicate isSource ( DataFlow:: Node source ) {
101
101
source .asExpr ( ) =
102
- any ( MethodAccess ma | setHttpOnlyInCookie ( ma ) or removeCookie ( ma ) ) .getQualifier ( )
102
+ any ( MethodAccess ma | setsCookieHttpOnly ( ma ) or removeCookie ( ma ) ) .getQualifier ( )
103
103
}
104
104
105
105
override predicate isSink ( DataFlow:: Node sink ) {
@@ -108,18 +108,18 @@ class SetHttpOnlyInCookieConfiguration extends TaintTracking2::Configuration {
108
108
}
109
109
}
110
110
111
- /** Holds if the method call removes a cookie. */
111
+ /** Holds if `ma` removes a cookie. */
112
112
predicate removeCookie ( MethodAccess ma ) {
113
113
ma .getMethod ( ) .getName ( ) = "setMaxAge" and
114
114
ma .getArgument ( 0 ) .( IntegerLiteral ) .getIntValue ( ) = 0
115
115
}
116
116
117
- /** Sensitive cookie name used in a `Cookie` constructor or a `Set-Cookie` call . */
117
+ /** A sensitive cookie name. */
118
118
class SensitiveCookieNameExpr extends Expr {
119
119
SensitiveCookieNameExpr ( ) { isSensitiveCookieNameExpr ( this ) }
120
120
}
121
121
122
- /** Sink of adding a cookie to the HTTP response. */
122
+ /** A cookie that is added to an HTTP response, used as a sink in `MissingHttpOnlyConfiguration` . */
123
123
class CookieResponseSink extends DataFlow:: ExprNode {
124
124
CookieResponseSink ( ) {
125
125
exists ( MethodAccess ma |
@@ -145,14 +145,14 @@ predicate setHttpOnlyInNewCookie(ClassInstanceExpr cie) {
145
145
cie .getConstructedType ( ) .hasQualifiedName ( [ "javax.ws.rs.core" , "jakarta.ws.rs.core" ] , "NewCookie" ) and
146
146
(
147
147
cie .getNumArgument ( ) = 6 and
148
- isBooleanTrue ( cie .getArgument ( 5 ) ) // NewCookie(Cookie cookie, String comment, int maxAge, Date expiry, boolean secure, boolean httpOnly)
148
+ mayBeBooleanTrue ( cie .getArgument ( 5 ) ) // NewCookie(Cookie cookie, String comment, int maxAge, Date expiry, boolean secure, boolean httpOnly)
149
149
or
150
150
cie .getNumArgument ( ) = 8 and
151
151
cie .getArgument ( 6 ) .getType ( ) instanceof BooleanType and
152
- isBooleanTrue ( cie .getArgument ( 7 ) ) // NewCookie(String name, String value, String path, String domain, String comment, int maxAge, boolean secure, boolean httpOnly)
152
+ mayBeBooleanTrue ( cie .getArgument ( 7 ) ) // NewCookie(String name, String value, String path, String domain, String comment, int maxAge, boolean secure, boolean httpOnly)
153
153
or
154
154
cie .getNumArgument ( ) = 10 and
155
- isBooleanTrue ( cie .getArgument ( 9 ) ) // NewCookie(String name, String value, String path, String domain, int version, String comment, int maxAge, Date expiry, boolean secure, boolean httpOnly)
155
+ mayBeBooleanTrue ( cie .getArgument ( 9 ) ) // NewCookie(String name, String value, String path, String domain, int version, String comment, int maxAge, Date expiry, boolean secure, boolean httpOnly)
156
156
)
157
157
}
158
158
0 commit comments