Skip to content

Commit 2ce5920

Browse files
Jami CogswellJami Cogswell
authored andcommitted
Java: copy out of experimental
1 parent 0d994c1 commit 2ce5920

File tree

8 files changed

+351
-0
lines changed

8 files changed

+351
-0
lines changed
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
@Configuration(proxyBeanMethods = false)
2+
public class SpringBootActuators extends WebSecurityConfigurerAdapter {
3+
4+
@Override
5+
protected void configure(HttpSecurity http) throws Exception {
6+
// BAD: Unauthenticated access to Spring Boot actuator endpoints is allowed
7+
http.requestMatcher(EndpointRequest.toAnyEndpoint()).authorizeRequests((requests) ->
8+
requests.anyRequest().permitAll());
9+
}
10+
}
11+
12+
@Configuration(proxyBeanMethods = false)
13+
public class ActuatorSecurity extends WebSecurityConfigurerAdapter {
14+
15+
@Override
16+
protected void configure(HttpSecurity http) throws Exception {
17+
// GOOD: only users with ENDPOINT_ADMIN role are allowed to access the actuator endpoints
18+
http.requestMatcher(EndpointRequest.toAnyEndpoint()).authorizeRequests((requests) ->
19+
requests.anyRequest().hasRole("ENDPOINT_ADMIN"));
20+
http.httpBasic();
21+
}
22+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<!DOCTYPE qhelp PUBLIC
2+
"-//Semmle//qhelp//EN"
3+
"qhelp.dtd">
4+
<qhelp>
5+
<overview>
6+
<p>Spring Boot includes a number of additional features called actuators that let you monitor
7+
and interact with your web application. Exposing unprotected actuator endpoints via JXM or HTTP
8+
can, however, lead to information disclosure or even to remote code execution vulnerability.</p>
9+
</overview>
10+
11+
<recommendation>
12+
<p>Since actuator endpoints may contain sensitive information, careful consideration should be
13+
given about when to expose them. You should take care to secure exposed HTTP endpoints in the same
14+
way that you would any other sensitive URL. If Spring Security is present, endpoints are secured by
15+
default using Spring Security’s content-negotiation strategy. If you wish to configure custom
16+
security for HTTP endpoints, for example, only allow users with a certain role to access them,
17+
Spring Boot provides some convenient <code>RequestMatcher</code> objects that can be used in
18+
combination with Spring Security.</p>
19+
</recommendation>
20+
21+
<example>
22+
<p>In the first example, the custom security configuration allows unauthenticated access to all
23+
actuator endpoints. This may lead to sensitive information disclosure and should be avoided.</p>
24+
<p>In the second example, only users with <code>ENDPOINT_ADMIN</code> role are allowed to access
25+
the actuator endpoints.</p>
26+
27+
<sample src="SpringBootActuators.java" />
28+
</example>
29+
30+
<references>
31+
<li>
32+
Spring Boot documentation:
33+
<a href="https://docs.spring.io/spring-boot/docs/current/reference/html/production-ready-features.html">Actuators</a>.
34+
</li>
35+
<li>
36+
<a href="https://www.veracode.com/blog/research/exploiting-spring-boot-actuators">Exploiting Spring Boot Actuators</a>
37+
</li>
38+
</references>
39+
</qhelp>
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/**
2+
* @name Exposed Spring Boot actuators
3+
* @description Exposing Spring Boot actuators may lead to internal application's information leak
4+
* or even to remote code execution.
5+
* @kind problem
6+
* @problem.severity error
7+
* @precision high
8+
* @id java/spring-boot-exposed-actuators
9+
* @tags security
10+
* experimental
11+
* external/cwe/cwe-16
12+
*/
13+
14+
import java
15+
deprecated import SpringBootActuators
16+
17+
deprecated query predicate problems(PermitAllCall permitAllCall, string message) {
18+
permitAllCall.permitsSpringBootActuators() and
19+
message = "Unauthenticated access to Spring Boot actuator is allowed."
20+
}
Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
deprecated module;
2+
3+
import java
4+
5+
/** The class `org.springframework.security.config.annotation.web.builders.HttpSecurity`. */
6+
class TypeHttpSecurity extends Class {
7+
TypeHttpSecurity() {
8+
this.hasQualifiedName("org.springframework.security.config.annotation.web.builders",
9+
"HttpSecurity")
10+
}
11+
}
12+
13+
/**
14+
* The class
15+
* `org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer`.
16+
*/
17+
class TypeAuthorizedUrl extends Class {
18+
TypeAuthorizedUrl() {
19+
this.hasQualifiedName("org.springframework.security.config.annotation.web.configurers",
20+
"ExpressionUrlAuthorizationConfigurer<HttpSecurity>$AuthorizedUrl<>")
21+
}
22+
}
23+
24+
/**
25+
* The class `org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry`.
26+
*/
27+
class TypeAbstractRequestMatcherRegistry extends Class {
28+
TypeAbstractRequestMatcherRegistry() {
29+
this.hasQualifiedName("org.springframework.security.config.annotation.web",
30+
"AbstractRequestMatcherRegistry<AuthorizedUrl<>>")
31+
}
32+
}
33+
34+
/**
35+
* The class `org.springframework.boot.actuate.autoconfigure.security.servlet.EndpointRequest`.
36+
*/
37+
class TypeEndpointRequest extends Class {
38+
TypeEndpointRequest() {
39+
this.hasQualifiedName("org.springframework.boot.actuate.autoconfigure.security.servlet",
40+
"EndpointRequest")
41+
}
42+
}
43+
44+
/** A call to `EndpointRequest.toAnyEndpoint` method. */
45+
class ToAnyEndpointCall extends MethodCall {
46+
ToAnyEndpointCall() {
47+
this.getMethod().hasName("toAnyEndpoint") and
48+
this.getMethod().getDeclaringType() instanceof TypeEndpointRequest
49+
}
50+
}
51+
52+
/**
53+
* A call to `HttpSecurity.requestMatcher` method with argument `RequestMatcher.toAnyEndpoint()`.
54+
*/
55+
class RequestMatcherCall extends MethodCall {
56+
RequestMatcherCall() {
57+
this.getMethod().hasName("requestMatcher") and
58+
this.getMethod().getDeclaringType() instanceof TypeHttpSecurity and
59+
this.getArgument(0) instanceof ToAnyEndpointCall
60+
}
61+
}
62+
63+
/**
64+
* A call to `HttpSecurity.requestMatchers` method with lambda argument
65+
* `RequestMatcher.toAnyEndpoint()`.
66+
*/
67+
class RequestMatchersCall extends MethodCall {
68+
RequestMatchersCall() {
69+
this.getMethod().hasName("requestMatchers") and
70+
this.getMethod().getDeclaringType() instanceof TypeHttpSecurity and
71+
this.getArgument(0).(LambdaExpr).getExprBody() instanceof ToAnyEndpointCall
72+
}
73+
}
74+
75+
/** A call to `HttpSecurity.authorizeRequests` method. */
76+
class AuthorizeRequestsCall extends MethodCall {
77+
AuthorizeRequestsCall() {
78+
this.getMethod().hasName("authorizeRequests") and
79+
this.getMethod().getDeclaringType() instanceof TypeHttpSecurity
80+
}
81+
}
82+
83+
/** A call to `AuthorizedUrl.permitAll` method. */
84+
class PermitAllCall extends MethodCall {
85+
PermitAllCall() {
86+
this.getMethod().hasName("permitAll") and
87+
this.getMethod().getDeclaringType() instanceof TypeAuthorizedUrl
88+
}
89+
90+
/** Holds if `permitAll` is called on request(s) mapped to actuator endpoint(s). */
91+
predicate permitsSpringBootActuators() {
92+
exists(AuthorizeRequestsCall authorizeRequestsCall |
93+
// .requestMatcher(EndpointRequest).authorizeRequests([...]).[...]
94+
authorizeRequestsCall.getQualifier() instanceof RequestMatcherCall
95+
or
96+
// .requestMatchers(matcher -> EndpointRequest).authorizeRequests([...]).[...]
97+
authorizeRequestsCall.getQualifier() instanceof RequestMatchersCall
98+
|
99+
// [...].authorizeRequests(r -> r.anyRequest().permitAll()) or
100+
// [...].authorizeRequests(r -> r.requestMatchers(EndpointRequest).permitAll())
101+
authorizeRequestsCall.getArgument(0).(LambdaExpr).getExprBody() = this and
102+
(
103+
this.getQualifier() instanceof AnyRequestCall or
104+
this.getQualifier() instanceof RegistryRequestMatchersCall
105+
)
106+
or
107+
// [...].authorizeRequests().requestMatchers(EndpointRequest).permitAll() or
108+
// [...].authorizeRequests().anyRequest().permitAll()
109+
authorizeRequestsCall.getNumArgument() = 0 and
110+
exists(RegistryRequestMatchersCall registryRequestMatchersCall |
111+
registryRequestMatchersCall.getQualifier() = authorizeRequestsCall and
112+
this.getQualifier() = registryRequestMatchersCall
113+
)
114+
or
115+
exists(AnyRequestCall anyRequestCall |
116+
anyRequestCall.getQualifier() = authorizeRequestsCall and
117+
this.getQualifier() = anyRequestCall
118+
)
119+
)
120+
or
121+
exists(AuthorizeRequestsCall authorizeRequestsCall |
122+
// http.authorizeRequests([...]).[...]
123+
authorizeRequestsCall.getQualifier() instanceof VarAccess
124+
|
125+
// [...].authorizeRequests(r -> r.requestMatchers(EndpointRequest).permitAll())
126+
authorizeRequestsCall.getArgument(0).(LambdaExpr).getExprBody() = this and
127+
this.getQualifier() instanceof RegistryRequestMatchersCall
128+
or
129+
// [...].authorizeRequests().requestMatchers(EndpointRequest).permitAll() or
130+
authorizeRequestsCall.getNumArgument() = 0 and
131+
exists(RegistryRequestMatchersCall registryRequestMatchersCall |
132+
registryRequestMatchersCall.getQualifier() = authorizeRequestsCall and
133+
this.getQualifier() = registryRequestMatchersCall
134+
)
135+
)
136+
}
137+
}
138+
139+
/** A call to `AbstractRequestMatcherRegistry.anyRequest` method. */
140+
class AnyRequestCall extends MethodCall {
141+
AnyRequestCall() {
142+
this.getMethod().hasName("anyRequest") and
143+
this.getMethod().getDeclaringType() instanceof TypeAbstractRequestMatcherRegistry
144+
}
145+
}
146+
147+
/**
148+
* A call to `AbstractRequestMatcherRegistry.requestMatchers` method with an argument
149+
* `RequestMatcher.toAnyEndpoint()`.
150+
*/
151+
class RegistryRequestMatchersCall extends MethodCall {
152+
RegistryRequestMatchersCall() {
153+
this.getMethod().hasName("requestMatchers") and
154+
this.getMethod().getDeclaringType() instanceof TypeAbstractRequestMatcherRegistry and
155+
this.getAnArgument() instanceof ToAnyEndpointCall
156+
}
157+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
| SpringBootActuators.java:6:88:6:120 | permitAll(...) | Unauthenticated access to Spring Boot actuator is allowed. |
2+
| SpringBootActuators.java:10:5:10:137 | permitAll(...) | Unauthenticated access to Spring Boot actuator is allowed. |
3+
| SpringBootActuators.java:14:5:14:149 | permitAll(...) | Unauthenticated access to Spring Boot actuator is allowed. |
4+
| SpringBootActuators.java:18:5:18:101 | permitAll(...) | Unauthenticated access to Spring Boot actuator is allowed. |
5+
| SpringBootActuators.java:22:5:22:89 | permitAll(...) | Unauthenticated access to Spring Boot actuator is allowed. |
6+
| SpringBootActuators.java:26:40:26:108 | permitAll(...) | Unauthenticated access to Spring Boot actuator is allowed. |
7+
| SpringBootActuators.java:30:5:30:113 | permitAll(...) | Unauthenticated access to Spring Boot actuator is allowed. |
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
import org.springframework.boot.actuate.autoconfigure.security.servlet.EndpointRequest;
2+
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
3+
4+
public class SpringBootActuators {
5+
protected void configure(HttpSecurity http) throws Exception {
6+
http.requestMatcher(EndpointRequest.toAnyEndpoint()).authorizeRequests(requests -> requests.anyRequest().permitAll());
7+
}
8+
9+
protected void configure2(HttpSecurity http) throws Exception {
10+
http.requestMatcher(EndpointRequest.toAnyEndpoint()).authorizeRequests().requestMatchers(EndpointRequest.toAnyEndpoint()).permitAll();
11+
}
12+
13+
protected void configure3(HttpSecurity http) throws Exception {
14+
http.requestMatchers(matcher -> EndpointRequest.toAnyEndpoint()).authorizeRequests().requestMatchers(EndpointRequest.toAnyEndpoint()).permitAll();
15+
}
16+
17+
protected void configure4(HttpSecurity http) throws Exception {
18+
http.requestMatcher(EndpointRequest.toAnyEndpoint()).authorizeRequests().anyRequest().permitAll();
19+
}
20+
21+
protected void configure5(HttpSecurity http) throws Exception {
22+
http.authorizeRequests().requestMatchers(EndpointRequest.toAnyEndpoint()).permitAll();
23+
}
24+
25+
protected void configure6(HttpSecurity http) throws Exception {
26+
http.authorizeRequests(requests -> requests.requestMatchers(EndpointRequest.toAnyEndpoint()).permitAll());
27+
}
28+
29+
protected void configure7(HttpSecurity http) throws Exception {
30+
http.requestMatchers(matcher -> EndpointRequest.toAnyEndpoint()).authorizeRequests().anyRequest().permitAll();
31+
}
32+
33+
protected void configureOk1(HttpSecurity http) throws Exception {
34+
http.requestMatcher(EndpointRequest.toAnyEndpoint());
35+
}
36+
37+
protected void configureOk2(HttpSecurity http) throws Exception {
38+
http.requestMatchers().requestMatchers(EndpointRequest.toAnyEndpoint());
39+
}
40+
41+
protected void configureOk3(HttpSecurity http) throws Exception {
42+
http.authorizeRequests().anyRequest().permitAll();
43+
}
44+
45+
protected void configureOk4(HttpSecurity http) throws Exception {
46+
http.authorizeRequests(authz -> authz.anyRequest().permitAll());
47+
}
48+
49+
protected void configureOkSafeEndpoints1(HttpSecurity http) throws Exception {
50+
http.requestMatcher(EndpointRequest.to("health", "info")).authorizeRequests(requests -> requests.anyRequest().permitAll());
51+
}
52+
53+
protected void configureOkSafeEndpoints2(HttpSecurity http) throws Exception {
54+
http.requestMatcher(EndpointRequest.to("health")).authorizeRequests().requestMatchers(EndpointRequest.to("health")).permitAll();
55+
}
56+
57+
protected void configureOkSafeEndpoints3(HttpSecurity http) throws Exception {
58+
http.requestMatchers(matcher -> EndpointRequest.to("health", "info")).authorizeRequests().requestMatchers(EndpointRequest.to("health", "info")).permitAll();
59+
}
60+
61+
protected void configureOkSafeEndpoints4(HttpSecurity http) throws Exception {
62+
http.requestMatcher(EndpointRequest.to("health", "info")).authorizeRequests().anyRequest().permitAll();
63+
}
64+
65+
protected void configureOkSafeEndpoints5(HttpSecurity http) throws Exception {
66+
http.authorizeRequests().requestMatchers(EndpointRequest.to("health", "info")).permitAll();
67+
}
68+
69+
protected void configureOkSafeEndpoints6(HttpSecurity http) throws Exception {
70+
http.authorizeRequests(requests -> requests.requestMatchers(EndpointRequest.to("health", "info")).permitAll());
71+
}
72+
73+
protected void configureOkSafeEndpoints7(HttpSecurity http) throws Exception {
74+
http.requestMatchers(matcher -> EndpointRequest.to("health", "info")).authorizeRequests().anyRequest().permitAll();
75+
}
76+
77+
protected void configureOkNoPermitAll1(HttpSecurity http) throws Exception {
78+
http.requestMatcher(EndpointRequest.toAnyEndpoint()).authorizeRequests(requests -> requests.anyRequest());
79+
}
80+
81+
protected void configureOkNoPermitAll2(HttpSecurity http) throws Exception {
82+
http.requestMatcher(EndpointRequest.toAnyEndpoint()).authorizeRequests().requestMatchers(EndpointRequest.toAnyEndpoint());
83+
}
84+
85+
protected void configureOkNoPermitAll3(HttpSecurity http) throws Exception {
86+
http.requestMatchers(matcher -> EndpointRequest.toAnyEndpoint()).authorizeRequests().requestMatchers(EndpointRequest.toAnyEndpoint());
87+
}
88+
89+
protected void configureOkNoPermitAll4(HttpSecurity http) throws Exception {
90+
http.requestMatcher(EndpointRequest.toAnyEndpoint()).authorizeRequests().anyRequest();
91+
}
92+
93+
protected void configureOkNoPermitAll5(HttpSecurity http) throws Exception {
94+
http.authorizeRequests().requestMatchers(EndpointRequest.toAnyEndpoint());
95+
}
96+
97+
protected void configureOkNoPermitAll6(HttpSecurity http) throws Exception {
98+
http.authorizeRequests(requests -> requests.requestMatchers(EndpointRequest.toAnyEndpoint()));
99+
}
100+
101+
protected void configureOkNoPermitAll7(HttpSecurity http) throws Exception {
102+
http.requestMatchers(matcher -> EndpointRequest.toAnyEndpoint()).authorizeRequests().anyRequest();
103+
}
104+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
experimental/Security/CWE/CWE-016/SpringBootActuators.ql
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
//semmle-extractor-options: --javac-args -cp ${testdir}/../../../../stubs/springframework-5.3.8

0 commit comments

Comments
 (0)