Recently, we have been particularly disturbed by various security vulnerabilities, and we receive dozens of emails a week from security teams scanning for vulnerabilities. One of these vulnerabilities is easy to overlook, but has a very wide impact and is extremely harmful. You shouldn’t be surprised when I say its name, it’s the Spring Boot Actuator.

Before writing this article, I did a little survey with my friends asking them about their knowledge of the Spring Boot Actuator and the results were amazingly consistent. Everyone knows that Spring Boot provides automatic configuration of the spring-boot-starter-actuator, but very few people actually use its related features. As you continue to read the article below, you can also think about the following questions.

  1. check whether you have introduced the spring-boot-starter-actuator dependency in your development project?
  2. do you actually use the spring-boot-starter-actuator functionality in your project?
  3. Do you know the security risks of spring-boot-starter-actuator and the correct way to configure it?

What is a Spring Boot Actuator?

It’s been a while since I’ve looked through the spring documentation, so to explain this fairly new term, Actutor [ˈæktjuˌeɪtər], I went through its documentation and found the official definition.

Definition of Actuator

An actuator is a manufacturing term that refers to a mechanical device for moving or controlling something. Actuators can generate a large amount of motion from a small change.

Quick Start

Step 1 Importing dependencies

tips: spring-boot-starter-actuator has some configuration differences in different versions of Spring Boot, this article is using version 2.4.4.

1
2
3
4
5
6

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
    <version>2.4.4</version>
</dependency>

Step 2 Understanding endpoint

The endpoint is the most important object we need to care about when using the Spring Boot Actuator, so here are some endpoints you might be interested in.

ID Description
beans View all objects in the Spring container
configprops View the list of objects modified by @ConfigurationProperties
env View the environment configuration information for the application.yaml configuration
health Health Check Endpoints
info Application Information
metrics Statistical Information
mappings Service contract @RequestMapping related endpoints
shutdown Graceful Shutdown

For example, for health, you only need to access the following endpoint to get the status of the application.

1
curl "localhost:8080/actuator/health"

Step 3 Understanding the enable and exposure states of endpoints

The Spring Boot Actuator provides two states of configuration for all endpoints

  • enabled state. By default, all endpoints are enabled except for shutdown. This is understandable, because while the other endpoints basically view behavior, shutdown affects the running state of the application.
  • Exposed state. endpoint enabled is set to true, it needs to be exposed once before it can be accessed. by default only health and info are exposed.

When enabled is not enabled, the code of the associated endpoint is not loaded by the Spring context at all, so it doesn’t matter if exposure is configured when enabled is false.

A few typical configuration examples are:

Enable and expose all endpoints.

1
2
3
4
5
6
7
8
9

management:
  endpoints:
    web:
      exposure:
        include: "*"
  endpoint:
    shutdown:
      enabled: true

Enable and expose only the specified endpoint.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
management:
  endpoints:
    enabled-by-default: false
  endpoint:
    info:
      enabled: true
  endpoints:
    web:
      exposure:
        include: "info"

Disable all endpoints.

1
2
3
management:
  endpoints:
    enabled-by-default: false

Or, get rid of the spring-boot-starter-actuator dependency!

Understanding the security risks of the Spring Boot Actuator

As you can see from the introduction above, there are endpoints provided by the Spring Boot Actuator that expose important information about the application, so let’s take a look at a typical application.yaml example using env as an example.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
server:
  port: 8080
spring:
  datasource:
   url: jdbc:mysql://testDbHost:3306/kirito
    username: kirito
    password: 123456
kirito:
  ak: kirito@xxx_ak
  sk: kirito@xxx_sk
management:
  endpoints:
    web:
      exposure:
        include: "*"

The above configuration couldn’t be more classic. Let’s look at the return value after accessing localhost:8080/actuator/env.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
{
  "activeProfiles": [],
  "propertySources": [
    {
      "name": "server.ports",
      "properties": {
        "local.server.port": {
          "value": 8080
        }
      }
    },
    {
      "name": "Config resource 'class path resource [application.yaml]' via location 'optional:classpath:/'",
      "properties": {
        "server.port": {
          "value": 8080,
          "origin": "class path resource [application.yaml] - 2:9"
        },
        "spring.datasource.url": {
          "value": "jdbc:mysql://testDbHost:3306/kirito",
          "origin": "class path resource [application.yaml] - 5:44"
        },
        "spring.datasource.username": {
          "value": "kirito",
          "origin": "class path resource [application.yaml] - 6:15"
        },
        "spring.datasource.password": {
          "value": "******",
          "origin": "class path resource [application.yaml] - 7:15"
        },
        "kirito.ak": {
          "value": "kirito@xxx_ak",
          "origin": "class path resource [application.yaml] - 10:7"
        },
        "kirito.sk": {
          "value": "kirito@xxx_sk",
          "origin": "class path resource [application.yaml] - 11:7"
        },
        "management.endpoints.web.exposure.include": {
          "value": "*",
          "origin": "class path resource [application.yaml] - 17:18"
        }
      }
    }
  ]
}

You can see that for the built-in sensitive configuration information spring.datasource.password, Spring Boot Actuator is desensitized, but for some custom sensitive configurations like kirito.ak and kirito.sk are exposed.

Some readers may immediately question this: our machines are deployed on intranets, and we generally expose services to the public through reverse proxies, so such endpoints are not accessible to external users. I can only say that it is too naive, for example, the following situations are the real case that lead to security vulnerabilities:

  • The reverse proxy misconfigures the root node and exposes the endpoint of the actuator together with the web service.
  • The online configuration is fine, but the public SLB is opened when the test environment is deployed, resulting in the actuator’s endpoint being exposed.
  • A machine in the same environment was compromised, resulting in the application configuration information leaked.

Security Recommendations

For the endpoint provided by the Spring Boot Actuator, there are several steps you can take to minimize the risk of security attacks.

  • Expose the endpoint with minimal granularity; only enable and expose the actual endpoint used, not the configuration: management.endpoints.web.exposure.include=*.
  • Configure a separate access port for the endpoint, so that it is separate from the web service’s port, to avoid exposing the web service and accidentally exposing the actuator’s endpoint as well. Example: management.port=8099.
  • Add a spring-boot-starter-security dependency to configure access control for the actuator’s endpoint.
  • Carefully evaluate the need to bring in spring-boot-stater-actuator. In my personal experience, I haven’t come across any requirements that necessarily require the introduction of spring-boot-stater-actuator to solve, so if you don’t understand the security risks described above, I suggest you remove the dependency first.

Reference https://mp.weixin.qq.com/s/tC-QjYZVMhCCvIsBX-z9zw