Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 29a5ea1

Browse files
authored
Merge pull request #2901 from ggolawski/java-spring-boot-actuators
CodeQL query to detect open Spring Boot actuator endpoints
2 parents 55301b4 + 639aa82 commit 29a5ea1

36 files changed

+467
-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: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
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+
* external/cwe/cwe-16
11+
*/
12+
13+
import java
14+
import SpringBootActuators
15+
16+
from PermitAllCall permitAllCall
17+
where permitAllCall.permitsSpringBootActuators()
18+
select permitAllCall, "Unauthenticated access to Spring Boot actuator is allowed."
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
import java
2+
3+
/** The class `org.springframework.security.config.annotation.web.builders.HttpSecurity`. */
4+
class TypeHttpSecurity extends Class {
5+
TypeHttpSecurity() {
6+
this
7+
.hasQualifiedName("org.springframework.security.config.annotation.web.builders",
8+
"HttpSecurity")
9+
}
10+
}
11+
12+
/**
13+
* The class
14+
* `org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer`.
15+
*/
16+
class TypeAuthorizedUrl extends Class {
17+
TypeAuthorizedUrl() {
18+
this
19+
.hasQualifiedName("org.springframework.security.config.annotation.web.configurers",
20+
"ExpressionUrlAuthorizationConfigurer<HttpSecurity>$AuthorizedUrl<>")
21+
}
22+
}
23+
24+
/**
25+
* The class
26+
* `org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry`.
27+
*/
28+
class TypeAbstractRequestMatcherRegistry extends Class {
29+
TypeAbstractRequestMatcherRegistry() {
30+
this
31+
.hasQualifiedName("org.springframework.security.config.annotation.web",
32+
"AbstractRequestMatcherRegistry<AuthorizedUrl<>>")
33+
}
34+
}
35+
36+
/**
37+
* The class
38+
* `org.springframework.boot.actuate.autoconfigure.security.servlet.EndpointRequest.EndpointRequestMatcher`.
39+
*/
40+
class TypeEndpointRequestMatcher extends Class {
41+
TypeEndpointRequestMatcher() {
42+
this
43+
.hasQualifiedName("org.springframework.boot.actuate.autoconfigure.security.servlet",
44+
"EndpointRequest$EndpointRequestMatcher")
45+
}
46+
}
47+
48+
/**
49+
* A call to `HttpSecurity.requestMatcher` method with argument of type
50+
* `EndpointRequestMatcher`.
51+
*/
52+
class RequestMatcherCall extends MethodAccess {
53+
RequestMatcherCall() {
54+
getMethod().hasName("requestMatcher") and
55+
getMethod().getDeclaringType() instanceof TypeHttpSecurity and
56+
getArgument(0).getType() instanceof TypeEndpointRequestMatcher
57+
}
58+
}
59+
60+
/**
61+
* A call to `HttpSecurity.requestMatchers` method with lambda argument resolving to
62+
* `EndpointRequestMatcher` type.
63+
*/
64+
class RequestMatchersCall extends MethodAccess {
65+
RequestMatchersCall() {
66+
getMethod().hasName("requestMatchers") and
67+
getMethod().getDeclaringType() instanceof TypeHttpSecurity and
68+
getArgument(0).(LambdaExpr).getExprBody().getType() instanceof TypeEndpointRequestMatcher
69+
}
70+
}
71+
72+
/** A call to `HttpSecurity.authorizeRequests` method. */
73+
class AuthorizeRequestsCall extends MethodAccess {
74+
AuthorizeRequestsCall() {
75+
getMethod().hasName("authorizeRequests") and
76+
getMethod().getDeclaringType() instanceof TypeHttpSecurity
77+
}
78+
}
79+
80+
/** A call to `AuthorizedUrl.permitAll` method. */
81+
class PermitAllCall extends MethodAccess {
82+
PermitAllCall() {
83+
getMethod().hasName("permitAll") and
84+
getMethod().getDeclaringType() instanceof TypeAuthorizedUrl
85+
}
86+
87+
/** Holds if `permitAll` is called on request(s) mapped to actuator endpoint(s). */
88+
predicate permitsSpringBootActuators() {
89+
exists(AuthorizeRequestsCall authorizeRequestsCall |
90+
// .requestMatcher(EndpointRequest).authorizeRequests([...]).[...]
91+
authorizeRequestsCall.getQualifier() instanceof RequestMatcherCall
92+
or
93+
// .requestMatchers(matcher -> EndpointRequest).authorizeRequests([...]).[...]
94+
authorizeRequestsCall.getQualifier() instanceof RequestMatchersCall
95+
or
96+
// http.authorizeRequests([...]).[...]
97+
authorizeRequestsCall.getQualifier() instanceof VarAccess
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+
}
121+
}
122+
123+
/** A call to `AbstractRequestMatcherRegistry.anyRequest` method. */
124+
class AnyRequestCall extends MethodAccess {
125+
AnyRequestCall() {
126+
getMethod().hasName("anyRequest") and
127+
getMethod().getDeclaringType() instanceof TypeAbstractRequestMatcherRegistry
128+
}
129+
}
130+
131+
/**
132+
* A call to `AbstractRequestMatcherRegistry.requestMatchers` method with an argument of type
133+
* `EndpointRequestMatcher`.
134+
*/
135+
class RegistryRequestMatchersCall extends MethodAccess {
136+
RegistryRequestMatchersCall() {
137+
getMethod().hasName("requestMatchers") and
138+
getMethod().getDeclaringType() instanceof TypeAbstractRequestMatcherRegistry and
139+
getAnArgument().getType() instanceof TypeEndpointRequestMatcher
140+
}
141+
}
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: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
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+
}
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.2.3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
package org.springframework.beans.factory;
2+
3+
public interface BeanFactory {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
package org.springframework.beans.factory;
2+
3+
public interface HierarchicalBeanFactory extends BeanFactory {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
package org.springframework.beans.factory;
2+
3+
public interface ListableBeanFactory extends BeanFactory {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package org.springframework.boot.actuate.autoconfigure.security.servlet;
2+
3+
import org.springframework.boot.security.servlet.ApplicationContextRequestMatcher;
4+
import org.springframework.web.context.WebApplicationContext;
5+
6+
public final class EndpointRequest {
7+
public static EndpointRequestMatcher toAnyEndpoint() {
8+
return null;
9+
}
10+
11+
public static final class EndpointRequestMatcher extends AbstractRequestMatcher {}
12+
13+
private abstract static class AbstractRequestMatcher
14+
extends ApplicationContextRequestMatcher<WebApplicationContext> {}
15+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package org.springframework.boot.security.servlet;
2+
3+
import org.springframework.security.web.util.matcher.RequestMatcher;
4+
5+
public abstract class ApplicationContextRequestMatcher<C> implements RequestMatcher {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package org.springframework.context;
2+
3+
import org.springframework.beans.factory.HierarchicalBeanFactory;
4+
import org.springframework.beans.factory.ListableBeanFactory;
5+
import org.springframework.core.env.EnvironmentCapable;
6+
import org.springframework.core.io.support.ResourcePatternResolver;
7+
8+
public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory,
9+
MessageSource, ApplicationEventPublisher, ResourcePatternResolver {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package org.springframework.context;
2+
3+
@FunctionalInterface
4+
public interface ApplicationEventPublisher {
5+
void publishEvent(Object event);
6+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
package org.springframework.context;
2+
3+
public interface MessageSource {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
package org.springframework.core.env;
2+
3+
public interface EnvironmentCapable {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
package org.springframework.core.io;
2+
3+
public interface ResourceLoader {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package org.springframework.core.io.support;
2+
3+
import org.springframework.core.io.ResourceLoader;
4+
5+
public interface ResourcePatternResolver extends ResourceLoader {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package org.springframework.security.config;
2+
3+
@FunctionalInterface
4+
public interface Customizer<T> {
5+
void customize(T t);
6+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
package org.springframework.security.config.annotation;
2+
3+
public abstract class AbstractConfiguredSecurityBuilder<O, B extends SecurityBuilder<O>>
4+
extends AbstractSecurityBuilder<O> {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
package org.springframework.security.config.annotation;
2+
3+
public abstract class AbstractSecurityBuilder<O> implements SecurityBuilder<O> {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
package org.springframework.security.config.annotation;
2+
3+
public interface SecurityBuilder<O> {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
package org.springframework.security.config.annotation;
2+
3+
public interface SecurityConfigurer<O, B extends SecurityBuilder<O>> {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
package org.springframework.security.config.annotation;
2+
3+
public abstract class SecurityConfigurerAdapter<O, B extends SecurityBuilder<O>>
4+
implements SecurityConfigurer<O, B> {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package org.springframework.security.config.annotation.web;
2+
3+
import org.springframework.security.web.util.matcher.RequestMatcher;
4+
5+
public abstract class AbstractRequestMatcherRegistry<C> {
6+
public C anyRequest() {
7+
return null;
8+
}
9+
10+
public C requestMatchers(RequestMatcher... requestMatchers) {
11+
return null;
12+
}
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package org.springframework.security.config.annotation.web;
2+
3+
import org.springframework.security.config.annotation.SecurityBuilder;
4+
import org.springframework.security.web.DefaultSecurityFilterChain;
5+
6+
public interface HttpSecurityBuilder<H extends HttpSecurityBuilder<H>> extends
7+
SecurityBuilder<DefaultSecurityFilterChain> {}

0 commit comments

Comments
 (0)