-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Java: Promote Insecure Spring Boot Actuator Configuration query from experimental #20006
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
a39cb40
0dbddbd
38260e7
fc930d9
ed8da5e
b479f5c
1b90a30
3823186
2bfc4b4
ae163a9
0d2a422
afa6610
ea35fbb
7d5e939
ea529b0
70d5150
8decc13
685f68d
7250265
0dd33b2
c9692a6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
/** Provides classes and predicates to reason about Spring Boot actuators exposed in configuration files. */ | ||
overlay[local?] | ||
module; | ||
|
||
import java | ||
private import semmle.code.configfiles.ConfigFiles | ||
private import semmle.code.xml.MavenPom | ||
|
||
/** The parent node of the `org.springframework.boot` group. */ | ||
private class SpringBootParent extends Parent { | ||
SpringBootParent() { this.getGroup().getValue() = "org.springframework.boot" } | ||
} | ||
|
||
/** A `Pom` with a Spring Boot parent node. */ | ||
private class SpringBootPom extends Pom { | ||
SpringBootPom() { this.getParentElement() instanceof SpringBootParent } | ||
|
||
/** Holds if the Spring Boot Security module is used in the project. */ | ||
predicate isSpringBootSecurityUsed() { | ||
this.getADependency().getArtifact().getValue() = "spring-boot-starter-security" | ||
} | ||
} | ||
|
||
/** A dependency with artifactId `spring-boot-starter-actuator`. */ | ||
class SpringBootStarterActuatorDependency extends Dependency { | ||
SpringBootStarterActuatorDependency() { | ||
this.getArtifact().getValue() = "spring-boot-starter-actuator" | ||
} | ||
} | ||
|
||
/** The Spring Boot configuration property `management.security.enabled`. */ | ||
private class ManagementSecurityEnabledProperty extends JavaProperty { | ||
ManagementSecurityEnabledProperty() { | ||
this.getNameElement().getName() = "management.security.enabled" | ||
} | ||
|
||
/** Gets the whitespace-trimmed value of this property. */ | ||
string getValue() { result = this.getValueElement().getValue().trim() } | ||
|
||
/** Holds if `management.security.enabled` is set to `false`. */ | ||
predicate hasSecurityDisabled() { this.getValue() = "false" } | ||
} | ||
|
||
/** | ||
* The Spring Boot configuration property `management.endpoints.web.exposure.include` | ||
* or `management.endpoints.web.expose`. | ||
*/ | ||
private class ManagementEndpointsExposeProperty extends JavaProperty { | ||
ManagementEndpointsExposeProperty() { | ||
this.getNameElement().getName() = "management.endpoints.web." + ["exposure.include", "expose"] | ||
} | ||
|
||
/** Gets the whitespace-trimmed value of this property. */ | ||
string getValue() { result = this.getValueElement().getValue().trim() } | ||
} | ||
|
||
private newtype TOption = | ||
TNone() or | ||
TSome(JavaProperty jp) | ||
|
||
/** | ||
* An option type that is either a singleton `None` or a `Some` wrapping | ||
* the `JavaProperty` type. | ||
*/ | ||
class JavaPropertyOption extends TOption { | ||
/** Gets a textual representation of this element. */ | ||
string toString() { | ||
this = TNone() and result = "(none)" | ||
or | ||
result = this.asSome().toString() | ||
} | ||
|
||
/** Gets the location of this element. */ | ||
Location getLocation() { result = this.asSome().getLocation() } | ||
|
||
/** Gets the wrapped element, if any. */ | ||
JavaProperty asSome() { this = TSome(result) } | ||
|
||
/** Holds if this option is the singleton `None`. */ | ||
predicate isNone() { this = TNone() } | ||
} | ||
|
||
/** | ||
* Holds if `JavaPropertyOption` jpOption of a repository using `SpringBootStarterActuatorDependency` | ||
* d exposes sensitive Spring Boot Actuator endpoints. | ||
*/ | ||
predicate exposesSensitiveEndpoint( | ||
SpringBootStarterActuatorDependency d, JavaPropertyOption jpOption | ||
) { | ||
exists(PropertiesFile propFile, SpringBootPom pom | | ||
d = pom.getADependency() and | ||
not pom.isSpringBootSecurityUsed() and | ||
propFile | ||
.getParentContainer() | ||
.getAbsolutePath() | ||
.matches(pom.getFile().getParentContainer().getAbsolutePath() + "%") and // in the same sub-directory | ||
exists(string springBootVersion | | ||
springBootVersion = pom.getParentElement().getVersionString() | ||
| | ||
springBootVersion.regexpMatch("1\\.[0-4].*") and // version 1.0, 1.1, ..., 1.4 | ||
not exists(ManagementSecurityEnabledProperty ep | ep.getFile() = propFile) and | ||
jpOption.isNone() | ||
or | ||
springBootVersion.regexpMatch("1\\.[0-5].*") and // version 1.0, 1.1, ..., 1.5 | ||
exists(ManagementSecurityEnabledProperty ep | | ||
ep.hasSecurityDisabled() and ep.getFile() = propFile and ep = jpOption.asSome() | ||
) | ||
or | ||
springBootVersion.matches(["2.%", "3.%"]) and //version 2.x and 3.x | ||
exists(ManagementEndpointsExposeProperty ep | | ||
ep.getFile() = propFile and | ||
ep = jpOption.asSome() and | ||
( | ||
// all endpoints are exposed | ||
ep.getValue() = "*" | ||
or | ||
// version 2.x: exposes health and info only by default | ||
springBootVersion.matches("2.%") and | ||
not ep.getValue() = ["health", "info"] | ||
or | ||
// version 3.x: exposes health only by default | ||
springBootVersion.matches("3.%") and | ||
not ep.getValue() = "health" | ||
) | ||
) | ||
) | ||
) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
<!DOCTYPE qhelp PUBLIC "-//Semmle//qhelp//EN" "qhelp.dtd"> | ||
<qhelp> | ||
<overview> | ||
<p>Spring Boot includes features called actuators that let you monitor and interact with your web | ||
application. Exposing unprotected actuator endpoints through configuration files can lead to | ||
information disclosure or even to remote code execution.</p> | ||
</overview> | ||
|
||
<recommendation> | ||
<p>Since actuator endpoints may contain sensitive information, carefully consider when to expose them, | ||
and secure them as you would any sensitive URL. If you need to expose actuator endpoints, use Spring | ||
Security, which secures actuators by default, or define a custom security configuration. | ||
</p> | ||
</recommendation> | ||
|
||
<example> | ||
<p>The following examples show <code>application.properties</code> configurations that expose sensitive | ||
actuator endpoints.</p> | ||
<sample src="application_bad.properties" /> | ||
|
||
<p>The below configurations ensure that sensitive actuator endpoints are not exposed.</p> | ||
<sample src="application_good.properties" /> | ||
|
||
<p>To use Spring Security, which secures actuators by default, add the <code>spring-boot-starter-security</code> | ||
dependency in your Maven <code>pom.xml</code> file.</p> | ||
<sample src="pom_good.xml" /> | ||
</example> | ||
|
||
<references> | ||
<li> | ||
Spring Boot Reference Documentation: | ||
<a href="https://docs.spring.io/spring-boot/reference/actuator/endpoints.html">Endpoints</a>. | ||
</li> | ||
<li> | ||
HackerOne Report: | ||
<a href="https://hackerone.com/reports/862589">Spring Actuator endpoints publicly available, leading to account takeover</a> | ||
</li> | ||
</references> | ||
</qhelp> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
/** | ||
* @name Exposed Spring Boot actuators in configuration file | ||
* @description Exposing Spring Boot actuators through configuration files may lead to information leak from | ||
* the internal application, or even to remote code execution. | ||
* @kind problem | ||
* @problem.severity error | ||
* @security-severity 6.5 | ||
* @precision high | ||
* @id java/spring-boot-exposed-actuators-config | ||
* @tags security | ||
* external/cwe/cwe-200 | ||
*/ | ||
|
||
import java | ||
import semmle.code.xml.MavenPom | ||
import semmle.code.java.security.SpringBootActuatorsConfigQuery | ||
|
||
from SpringBootStarterActuatorDependency d, JavaPropertyOption jpOption | ||
where exposesSensitiveEndpoint(d, jpOption) | ||
select d, "Insecure Spring Boot actuator $@ exposes sensitive endpoints.", jpOption, "configuration" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm conflicted about whether the alert location should be on the dependency in the pom file versus on the property in the configuration file. If the developer wants to fix the issue by adding Spring Security to the classpath, then they need to edit the pom file, so in that case the alert in the pom file makes sense, but if the developer wants to instead edit the configuration in the |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
# vulnerable configuration (Spring Boot 1.0 - 1.4): exposes endpoints by default | ||
|
||
# vulnerable configuration (Spring Boot 1.5): false value exposes endpoints | ||
management.security.enabled=false | ||
|
||
# vulnerable configuration (Spring Boot 2.x): exposes all endpoints | ||
management.endpoints.web.exposure.include=* | ||
|
||
# vulnerable configuration (Spring Boot 3.x): exposes all endpoints | ||
management.endpoints.web.exposure.include=* |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
# safe configuration (Spring Boot 1.0 - 1.4) | ||
management.security.enabled=true | ||
|
||
# safe configuration (Spring Boot 1.5+) | ||
management.security.enabled=true | ||
|
||
# safe configuration (Spring Boot 2.x): exposes health and info only by default | ||
management.endpoints.web.exposure.include=health,info | ||
|
||
# safe configuration (Spring Boot 3.x): exposes health only by default | ||
management.endpoints.web.exposure.include=health |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
... | ||
<dependency> | ||
<groupId>org.springframework.boot</groupId> | ||
<artifactId>spring-boot-starter-actuator</artifactId> | ||
</dependency> | ||
|
||
<!-- GOOD: Enable Spring Security --> | ||
<dependency> | ||
<groupId>org.springframework.boot</groupId> | ||
<artifactId>spring-boot-starter-security</artifactId> | ||
</dependency> | ||
... |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
--- | ||
category: newQuery | ||
--- | ||
* The query `java/insecure-spring-actuator-config` has been promoted from experimental to the main query pack as `java/spring-boot-exposed-actuators-config`. Its results will now appear by default. This query was originally submitted as an experimental query [by @luchua-bc](https://github.com/github/codeql/pull/5384). |
This file was deleted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've changed this from checking for a specific list of endpoints to checking for not health/info to align with what the Spring documentation considers to be potentially sensitive endpoints. Let me know if you think this errs too much towards FPs, and I can change it back. This change only added 7 results in MRVA.