mirror of
https://github.com/github/codeql.git
synced 2026-05-01 03:35:13 +02:00
Query to detect insecure configuration of Spring Boot Actuator
This commit is contained in:
@@ -0,0 +1,47 @@
|
||||
<!DOCTYPE qhelp PUBLIC "-//Semmle//qhelp//EN" "qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>Spring Boot is a popular framework that facilitates the development of stand-alone applications
|
||||
and micro services. Spring Boot Actuator helps to expose production-ready support features against
|
||||
Spring Boot applications.</p>
|
||||
|
||||
<p>Endpoints of Spring Boot Actuator allow to monitor and interact with a Spring Boot application.
|
||||
Exposing unprotected actuator endpoints through configuration files can lead to information disclosure
|
||||
or even remote code execution vulnerability.</p>
|
||||
|
||||
<p>Rather than programmatically permitting endpoint requests or enforcing access control, frequently
|
||||
developers simply leave management endpoints publicly accessible in the application configuration file
|
||||
<code>application.properties</code> without enforcing access control through Spring Security.</p>
|
||||
</overview>
|
||||
|
||||
<recommendation>
|
||||
<p>Declare the Spring Boot Starter Security module in XML configuration or programmatically enforce
|
||||
security checks on management endpoints using Spring Security. Otherwise accessing management endpoints
|
||||
on a different HTTP port other than the port that the web application is listening on also helps to
|
||||
improve the security.</p>
|
||||
</recommendation>
|
||||
|
||||
<example>
|
||||
<p>The following examples show both 'BAD' and 'GOOD' configurations. In the 'BAD' configuration,
|
||||
no security module is declared and sensitive management endpoints are exposed. In the 'GOOD' configuration,
|
||||
security is enforced and only endpoints requiring exposure are exposed.</p>
|
||||
<sample src="pom_good.xml" />
|
||||
<sample src="pom_bad.xml" />
|
||||
<sample src="application.properties" />
|
||||
</example>
|
||||
|
||||
<references>
|
||||
<li>
|
||||
Spring Boot documentation:
|
||||
<a href="https://docs.spring.io/spring-boot/docs/current/reference/html/production-ready-features.html">Spring Boot Actuator: Production-ready Features</a>
|
||||
</li>
|
||||
<li>
|
||||
VERACODE Blog:
|
||||
<a href="https://www.veracode.com/blog/research/exploiting-spring-boot-actuators">Exploiting Spring Boot Actuators</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>
|
||||
@@ -0,0 +1,112 @@
|
||||
/**
|
||||
* @name Insecure Spring Boot Actuator Configuration
|
||||
* @description Exposed Spring Boot Actuator through configuration files without declarative or procedural security enforcement leads to information leak or even remote code execution.
|
||||
* @kind problem
|
||||
* @id java/insecure-spring-actuator-config
|
||||
* @tags security
|
||||
* external/cwe-016
|
||||
*/
|
||||
|
||||
import java
|
||||
import semmle.code.configfiles.ConfigFiles
|
||||
import semmle.code.java.security.SensitiveActions
|
||||
import semmle.code.xml.MavenPom
|
||||
|
||||
/** The parent node of the `org.springframework.boot` group. */
|
||||
class SpringBootParent extends Parent {
|
||||
SpringBootParent() { this.getGroup().getValue() = "org.springframework.boot" }
|
||||
}
|
||||
|
||||
/** Class of Spring Boot dependencies. */
|
||||
class SpringBootPom extends Pom {
|
||||
SpringBootPom() { this.getParentElement() instanceof SpringBootParent }
|
||||
|
||||
/** Holds if the Spring Boot Actuator module `spring-boot-starter-actuator` is used in the project. */
|
||||
predicate isSpringBootActuatorUsed() {
|
||||
this.getADependency().getArtifact().getValue() = "spring-boot-starter-actuator"
|
||||
}
|
||||
|
||||
/** Holds if the Spring Boot Security module is used in the project, which brings in other security related libraries. */
|
||||
predicate isSpringBootSecurityUsed() {
|
||||
this.getADependency().getArtifact().getValue() = "spring-boot-starter-security"
|
||||
}
|
||||
}
|
||||
|
||||
/** The properties file `application.properties`. */
|
||||
class ApplicationProperties extends ConfigPair {
|
||||
ApplicationProperties() { this.getFile().getBaseName() = "application.properties" }
|
||||
}
|
||||
|
||||
/** The configuration property `management.security.enabled`. */
|
||||
class ManagementSecurityEnabled extends ApplicationProperties {
|
||||
ManagementSecurityEnabled() { this.getNameElement().getName() = "management.security.enabled" }
|
||||
|
||||
string getManagementSecurityEnabled() { result = this.getValueElement().getValue() }
|
||||
|
||||
predicate hasSecurityDisabled() { getManagementSecurityEnabled() = "false" }
|
||||
|
||||
predicate hasSecurityEnabled() { getManagementSecurityEnabled() = "true" }
|
||||
}
|
||||
|
||||
/** The configuration property `management.endpoints.web.exposure.include`. */
|
||||
class ManagementEndPointInclude extends ApplicationProperties {
|
||||
ManagementEndPointInclude() {
|
||||
this.getNameElement().getName() = "management.endpoints.web.exposure.include"
|
||||
}
|
||||
|
||||
string getManagementEndPointInclude() { result = this.getValueElement().getValue().trim() }
|
||||
}
|
||||
|
||||
/** The configuration property `management.endpoints.web.exposure.exclude`. */
|
||||
class ManagementEndPointExclude extends ApplicationProperties {
|
||||
ManagementEndPointExclude() {
|
||||
this.getNameElement().getName() = "management.endpoints.web.exposure.exclude"
|
||||
}
|
||||
|
||||
string getManagementEndPointExclude() { result = this.getValueElement().getValue().trim() }
|
||||
}
|
||||
|
||||
/** Holds if an application handles sensitive information judging by its variable names. */
|
||||
predicate isProtectedApp() {
|
||||
exists(VarAccess va | va.getVariable().getName().regexpMatch(getCommonSensitiveInfoRegex()))
|
||||
}
|
||||
|
||||
from SpringBootPom pom, ApplicationProperties ap, Dependency d
|
||||
where
|
||||
isProtectedApp() and
|
||||
pom.isSpringBootActuatorUsed() and
|
||||
not pom.isSpringBootSecurityUsed() and
|
||||
ap.getFile()
|
||||
.getParentContainer()
|
||||
.getAbsolutePath()
|
||||
.matches(pom.getFile().getParentContainer().getAbsolutePath() + "%") and // in the same sub-directory
|
||||
exists(string s | s = pom.getParentElement().getVersionString() |
|
||||
s.regexpMatch("1\\.[0|1|2|3|4].*") and
|
||||
not exists(ManagementSecurityEnabled me |
|
||||
me.hasSecurityEnabled() and me.getFile() = ap.getFile()
|
||||
)
|
||||
or
|
||||
s.regexpMatch("1\\.5.*") and
|
||||
exists(ManagementSecurityEnabled me | me.hasSecurityDisabled() and me.getFile() = ap.getFile())
|
||||
or
|
||||
s.regexpMatch("2.*") and
|
||||
exists(ManagementEndPointInclude mi |
|
||||
mi.getFile() = ap.getFile() and
|
||||
(
|
||||
mi.getManagementEndPointInclude() = "*" // all endpoints are enabled
|
||||
or
|
||||
mi.getManagementEndPointInclude()
|
||||
.matches([
|
||||
"%dump%", "%trace%", "%logfile%", "%shutdown%", "%startup%", "%mappings%", "%env%",
|
||||
"%beans%", "%sessions%"
|
||||
]) // all endpoints apart from '/health' and '/info' are considered sensitive
|
||||
) and
|
||||
not exists(ManagementEndPointExclude mx |
|
||||
mx.getFile() = ap.getFile() and
|
||||
mx.getManagementEndPointExclude() = mi.getManagementEndPointInclude()
|
||||
)
|
||||
)
|
||||
) and
|
||||
d = pom.getADependency() and
|
||||
d.getArtifact().getValue() = "spring-boot-starter-actuator"
|
||||
select d, "Insecure configuration of Spring Boot Actuator exposes sensitive endpoints."
|
||||
@@ -0,0 +1,22 @@
|
||||
#management.endpoints.web.base-path=/admin
|
||||
|
||||
|
||||
#### BAD: All management endpoints are accessible ####
|
||||
# vulnerable configuration (spring boot 1.0 - 1.4): exposes actuators by default
|
||||
|
||||
# vulnerable configuration (spring boot 1.5+): requires value false to expose sensitive actuators
|
||||
management.security.enabled=false
|
||||
|
||||
# vulnerable configuration (spring boot 2+): exposes health and info only by default
|
||||
management.endpoints.web.exposure.include=*
|
||||
|
||||
|
||||
#### GOOD: All management endpoints have access control ####
|
||||
# safe configuration (spring boot 1.0 - 1.4): exposes actuators by default
|
||||
management.security.enabled=true
|
||||
|
||||
# safe configuration (spring boot 1.5+): requires value false to expose sensitive actuators
|
||||
management.security.enabled=true
|
||||
|
||||
# safe configuration (spring boot 2+): exposes health and info only by default
|
||||
management.endpoints.web.exposure.include=beans,info,health
|
||||
50
java/ql/src/experimental/Security/CWE/CWE-016/pom_bad.xml
Normal file
50
java/ql/src/experimental/Security/CWE/CWE-016/pom_bad.xml
Normal file
@@ -0,0 +1,50 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>spring-boot-actuator-app</groupId>
|
||||
<artifactId>spring-boot-actuator-app</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<maven.compiler.source>1.8</maven.compiler.source>
|
||||
<maven.compiler.target>1.8</maven.compiler.target>
|
||||
</properties>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.3.8.RELEASE</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-devtools</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- BAD: No Spring Security enabled -->
|
||||
<!-- dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-security</artifactId>
|
||||
</dependency -->
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-test</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
50
java/ql/src/experimental/Security/CWE/CWE-016/pom_good.xml
Normal file
50
java/ql/src/experimental/Security/CWE/CWE-016/pom_good.xml
Normal file
@@ -0,0 +1,50 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>spring-boot-actuator-app</groupId>
|
||||
<artifactId>spring-boot-actuator-app</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<maven.compiler.source>1.8</maven.compiler.source>
|
||||
<maven.compiler.target>1.8</maven.compiler.target>
|
||||
</properties>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.3.8.RELEASE</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-devtools</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- GOOD: Enable Spring Security -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-security</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-test</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1 @@
|
||||
| pom.xml:29:9:32:22 | dependency | Insecure configuration of Spring Boot Actuator exposes sensitive endpoints. |
|
||||
@@ -0,0 +1 @@
|
||||
experimental/Security/CWE/CWE-016/InsecureSpringActuatorConfig.ql
|
||||
@@ -0,0 +1,13 @@
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
|
||||
@Controller
|
||||
public class SensitiveInfo {
|
||||
@RequestMapping
|
||||
public void handleLogin(@RequestParam String username, @RequestParam String password) throws Exception {
|
||||
if (!username.equals("") && password.equals("")) {
|
||||
//Blank processing
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
#management.endpoints.web.base-path=/admin
|
||||
|
||||
# vulnerable configuration (spring boot 1.0 - 1.4): exposes actuators by default
|
||||
|
||||
# vulnerable configuration (spring boot 1.5+): requires value false to expose sensitive actuators
|
||||
management.security.enabled=false
|
||||
|
||||
# vulnerable configuration (spring boot 2+): exposes health and info only by default
|
||||
management.endpoints.web.exposure.include=*
|
||||
management.endpoints.web.exposure.exclude=beans
|
||||
|
||||
management.endpoint.shutdown.enabled=true
|
||||
|
||||
management.endpoint.health.show-details=when_authorized
|
||||
@@ -0,0 +1,47 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>spring-boot-actuator-app</groupId>
|
||||
<artifactId>spring-boot-actuator-app</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<maven.compiler.source>1.8</maven.compiler.source>
|
||||
<maven.compiler.target>1.8</maven.compiler.target>
|
||||
</properties>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.3.8.RELEASE</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-devtools</artifactId>
|
||||
</dependency>
|
||||
<!-- dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-security</artifactId>
|
||||
</dependency -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-test</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
Reference in New Issue
Block a user