Skip to content

Commit 985d3d4

Browse files
committed
PR feedback integration
1 parent 75b7903 commit 985d3d4

2 files changed

Lines changed: 29 additions & 26 deletions

File tree

java/ql/src/Security/CWE/CWE-346/UnvalidatedCors.qhelp

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,20 +7,20 @@
77
<p>
88

99
A server can send the
10-
<code>"Access-Control-Allow-Credentials"</code> CORS header to control
10+
<code>Access-Control-Allow-Credentials</code> CORS header to control
1111
when a browser may send user credentials in Cross-Origin HTTP
1212
requests.
1313

1414
</p>
1515
<p>
1616

1717
When the <code>Access-Control-Allow-Credentials</code> header
18-
is <code>"true"</code>, the <code>Access-Control-Allow-Origin</code>
19-
header must have a value different from <code>"*"</code> in order to
18+
is <code>true</code>, the <code>Access-Control-Allow-Origin</code>
19+
header must have a value different from <code>*</code> in order to
2020
make browsers accept the header. Therefore, to allow multiple origins
2121
for Cross-Origin requests with credentials, the server must
2222
dynamically compute the value of the
23-
<code>"Access-Control-Allow-Origin"</code> header. Computing this
23+
<code>Access-Control-Allow-Origin</code> header. Computing this
2424
header value from information in the request to the server can
2525
therefore potentially allow an attacker to control the origins that
2626
the browser sends credentials to.
@@ -35,19 +35,20 @@
3535
<p>
3636

3737
When the <code>Access-Control-Allow-Credentials</code> header
38-
value is <code>"true"</code>, a dynamic computation of the
38+
value is <code>true</code>, a dynamic computation of the
3939
<code>Access-Control-Allow-Origin</code> header must involve
4040
sanitization if it relies on user-controlled input.
4141

4242

4343
</p>
4444
<p>
4545

46-
Since the <code>"null"</code> origin is easy to obtain for an
47-
attacker, it is never safe to use <code>"null"</code> as the value of
46+
Since the <code>null</code> origin is easy to obtain for an
47+
attacker, it is never safe to use <code>null</code> as the value of
4848
the <code>Access-Control-Allow-Origin</code> header when the
4949
<code>Access-Control-Allow-Credentials</code> header value is
50-
<code>"true"</code>.
50+
<code>true</code>.This can be done using a sandboxed iframe. A more detailed
51+
explanation is available in the portswigger blogpost.
5152

5253
</p>
5354
</recommendation>

java/ql/src/Security/CWE/CWE-346/UnvalidatedCors.ql

Lines changed: 20 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,17 @@ import semmle.code.java.frameworks.Servlets
1515
import semmle.code.java.dataflow.TaintTracking
1616
import DataFlow::PathGraph
1717

18-
// Check for Access-Control-Allow-Credentials as well, this ensures fair chances of exploitability.
19-
predicate satisfyAllowCredentials(MethodAccess header, MethodAccess check) {
18+
/**
19+
* Holds if `header` sets `Access-Control-Allow-Credentials` to `true`. This ensures fair chances of exploitability.
20+
*/
21+
private predicate setsAllowCredentials(MethodAccess header) {
2022
header.getArgument(0).(CompileTimeConstantExpr).getStringValue().toLowerCase() =
2123
"access-control-allow-credentials" and
22-
header.getArgument(1).(CompileTimeConstantExpr).getStringValue() = "true" and
23-
header.getEnclosingCallable() = check.getEnclosingCallable()
24+
header.getArgument(1).(CompileTimeConstantExpr).getStringValue() = "true"
2425
}
2526

26-
predicate checkAccessControlAllowOriginHeader(Expr expr) {
27-
expr.(CompileTimeConstantExpr).getStringValue().toLowerCase() = "access-control-allow-origin"
27+
private Expr getAccessControlAllowOriginHeaderName() {
28+
result.(CompileTimeConstantExpr).getStringValue().toLowerCase() = "access-control-allow-origin"
2829
}
2930

3031
class CorsOriginConfig extends TaintTracking::Configuration {
@@ -33,18 +34,19 @@ class CorsOriginConfig extends TaintTracking::Configuration {
3334
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
3435

3536
override predicate isSink(DataFlow::Node sink) {
36-
exists(ResponseSetHeaderMethod h, MethodAccess m |
37-
m = h.getAReference() and
38-
checkAccessControlAllowOriginHeader(m.getArgument(0)) and
39-
satisfyAllowCredentials(h.getAReference(), m) and
40-
sink.asExpr() = m.getArgument(1)
41-
)
42-
or
43-
exists(ResponseAddHeaderMethod a, MethodAccess m |
44-
m = a.getAReference() and
45-
checkAccessControlAllowOriginHeader(m.getArgument(0)) and
46-
satisfyAllowCredentials(a.getAReference(), m) and
47-
sink.asExpr() = m.getArgument(1)
37+
exists(MethodAccess corsheader, MethodAccess allowcredentialsheader |
38+
(
39+
corsheader.getMethod() instanceof ResponseSetHeaderMethod or
40+
corsheader.getMethod() instanceof ResponseAddHeaderMethod
41+
) and
42+
(
43+
allowcredentialsheader.getMethod() instanceof ResponseSetHeaderMethod or
44+
allowcredentialsheader.getMethod() instanceof ResponseAddHeaderMethod
45+
) and
46+
getAccessControlAllowOriginHeaderName() = corsheader.getArgument(0) and
47+
setsAllowCredentials(allowcredentialsheader) and
48+
corsheader.getEnclosingCallable() = allowcredentialsheader.getEnclosingCallable() and
49+
sink.asExpr() = corsheader.getArgument(1)
4850
)
4951
}
5052
}

0 commit comments

Comments
 (0)