Spring Boot Actuator暴露敏感端点的安全加固方案

Spring Boot Actuator 暴露敏感端点的安全加固方案

大家好,今天我们来聊聊Spring Boot Actuator的安全加固,重点关注如何保护那些可能暴露敏感信息的端点。Actuator为我们提供了监控和管理Spring Boot应用的能力,但如果配置不当,它也会成为安全漏洞的入口。

Actuator 简介

Spring Boot Actuator是一个强大的工具,可以帮助你监控和管理你的应用程序。它提供了一系列的端点,用于暴露应用的内部状态、配置信息、健康状况等。

常见的Actuator端点包括:

端点 描述 默认暴露情况
/health 显示应用程序的健康状况。 公开
/info 显示应用程序的构建信息、版本号等。 公开
/metrics 显示应用程序的各种指标,例如JVM内存使用情况、HTTP请求统计等。 公开
/env 显示应用程序的环境变量和系统属性。敏感信息可能泄露! 私有
/configprops 显示应用程序的所有配置属性。敏感信息可能泄露! 私有
/beans 显示应用程序中定义的所有Spring Beans。 私有
/mappings 显示应用程序的所有请求映射。 私有
/threaddump 显示应用程序的线程转储。 私有
/heapdump 生成应用程序的堆转储。 私有
/loggers 获取和修改应用程序的日志级别。 私有
/auditevents 显示应用程序的审计事件。 私有
/httptrace 显示最近的HTTP请求和响应。 私有

默认情况下,/health/info是公开的(可以通过HTTP请求直接访问),其他的端点都是私有的,需要认证才能访问。但是,如果你的配置不当,可能会意外地将敏感端点暴露给未经授权的用户。

风险分析

未加保护的Actuator端点可能导致以下安全风险:

  • 敏感信息泄露: /env/configprops等端点可能暴露数据库密码、API密钥等敏感信息。
  • 信息收集: 攻击者可以通过/beans/mappings等端点了解应用程序的内部结构和配置,为进一步攻击做准备。
  • 拒绝服务(DoS): /heapdump等端点可能消耗大量资源,导致应用程序崩溃。
  • 恶意配置修改: /loggers端点允许修改日志级别,可能被用于隐藏攻击行为。

安全加固方案

以下是一些加固Actuator安全性的方案,可以根据你的实际需求选择合适的组合:

1. 禁用不必要的端点

最简单的安全措施是禁用你不需要的端点。这可以通过在application.propertiesapplication.yml文件中设置management.endpoint.<endpoint>.enabled=false来实现。

例如,禁用/env/configprops端点:

management.endpoint.env.enabled=false
management.endpoint.configprops.enabled=false

或者,使用YAML格式:

management:
  endpoint:
    env:
      enabled: false
    configprops:
      enabled: false

2. 使用Spring Security进行认证和授权

Spring Security是保护Actuator端点最常用的方法。你需要添加Spring Security依赖到你的项目中:

Maven:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

Gradle:

implementation 'org.springframework.boot:spring-boot-starter-security'

然后,你需要配置Spring Security来保护Actuator端点。以下是一个基本的配置示例:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http.csrf().disable()
                .authorizeHttpRequests(authorize -> authorize
                        .requestMatchers("/actuator/**").hasRole("ACTUATOR")
                        .anyRequest().permitAll()
                )
                .httpBasic();
        return http.build();
    }
}

这个配置做了以下事情:

  • 禁用了CSRF保护(Actuator端点通常不需要CSRF保护)。
  • 要求访问/actuator/**下的所有端点都需要ACTUATOR角色。
  • 允许访问所有其他端点。
  • 使用HTTP Basic认证。

你还需要配置一个用户,并赋予其ACTUATOR角色。这可以通过在application.propertiesapplication.yml文件中配置spring.security.user.namespring.security.user.passwordspring.security.user.roles来实现。

例如:

spring.security.user.name=actuator
spring.security.user.password=password
spring.security.user.roles=ACTUATOR

或者,使用YAML格式:

spring:
  security:
    user:
      name: actuator
      password: password
      roles: ACTUATOR

更细粒度的授权控制:

如果需要更细粒度的控制,可以为不同的Actuator端点配置不同的角色。例如,只有管理员才能访问/heapdump端点。

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http.csrf().disable()
                .authorizeHttpRequests(authorize -> authorize
                        .requestMatchers("/actuator/health", "/actuator/info").permitAll()
                        .requestMatchers("/actuator/heapdump").hasRole("ADMIN")
                        .requestMatchers("/actuator/**").hasRole("ACTUATOR")
                        .anyRequest().permitAll()
                )
                .httpBasic();
        return http.build();
    }
}

在这个例子中,/health/info端点不需要认证,/heapdump端点需要ADMIN角色,其他的Actuator端点需要ACTUATOR角色。

3. 使用自定义的 Management Port

默认情况下,Actuator端点与应用程序的HTTP端口共享。为了增加安全性,你可以将Actuator端点配置在不同的端口上。这可以通过在application.propertiesapplication.yml文件中设置management.server.port来实现。

例如,将Actuator端点配置在端口8081上:

management.server.port=8081

或者,使用YAML格式:

management:
  server:
    port: 8081

然后,你可以使用防火墙规则来限制对Actuator端口的访问,只允许特定的IP地址或网络访问。

注意:

  • 如果management.server.port设置为${server.port},则Actuator端点将与应用程序的HTTP端口共享。
  • 如果management.server.port设置为0,则Actuator端点将使用一个随机端口。

4. 使用IP地址白名单

如果你不需要公开Actuator端点,可以使用IP地址白名单来限制对Actuator端点的访问。这可以通过在application.propertiesapplication.yml文件中设置management.endpoint.health.rolesmanagement.endpoint.info.roles 并结合Spring Security来实现。

首先,你需要启用 Spring Security,并配置一个用户和角色。

spring.security.user.name=actuator
spring.security.user.password=password
spring.security.user.roles=ACTUATOR

然后,配置Actuator端点,需要特定的角色才能访问

management.endpoint.health.roles=ACTUATOR
management.endpoint.info.roles=ACTUATOR

然后,配置 Spring Security 来允许特定的 IP 地址访问 Actuator 端点,可以通过实现一个自定义的 Filter 来实现:

import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import org.springframework.stereotype.Component;

@Component
public class IPAddressFilter implements Filter {

  private final List<String> allowedIPs = Arrays.asList("127.0.0.1", "0:0:0:0:0:0:0:1");

  @Override
  public void doFilter(
      ServletRequest request, ServletResponse response, FilterChain chain)
      throws IOException, ServletException {
    HttpServletRequest httpRequest = (HttpServletRequest) request;
    HttpServletResponse httpResponse = (HttpServletResponse) response;

    String clientIP = httpRequest.getRemoteAddr();

    if (allowedIPs.contains(clientIP)) {
      chain.doFilter(request, response);
    } else {
      httpResponse.setStatus(HttpServletResponse.SC_FORBIDDEN);
      httpResponse.getWriter().write("Access Denied: Your IP is not allowed.");
    }
  }
}

最后,将这个 Filter 注册到 Spring Security 的 Filter Chain 中:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.access.channel.ChannelProcessingFilter;

@Configuration
@EnableWebSecurity
public class SecurityConfig {

  @Autowired private IPAddressFilter ipAddressFilter;

  @Bean
  public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    http.csrf()
        .disable()
        .authorizeHttpRequests(
            authorize ->
                authorize
                    .requestMatchers("/actuator/health", "/actuator/info")
                    .hasRole("ACTUATOR")
                    .anyRequest()
                    .permitAll())
        .httpBasic();

    http.addFilterBefore(ipAddressFilter, ChannelProcessingFilter.class);

    return http.build();
  }
}

5. 定制端点暴露

Spring Boot 2.x 引入了新的方式来控制端点的暴露,更加灵活,也更安全。我们可以使用 management.endpoints.web.exposure.includemanagement.endpoints.web.exposure.exclude 属性来控制哪些端点可以通过 Web 暴露。

  • management.endpoints.web.exposure.include: 指定要暴露的端点 ID 列表。 可以使用 * 暴露所有端点。
  • management.endpoints.web.exposure.exclude: 指定要排除的端点 ID 列表。

例如,只暴露/health/info端点:

management.endpoints.web.exposure.include=health,info

或者,使用YAML格式:

management:
  endpoints:
    web:
      exposure:
        include: health,info

这样,即使没有配置Spring Security,也只有/health/info端点可以通过Web访问。

如果你想暴露所有端点,但是排除/env/configprops端点:

management.endpoints.web.exposure.include=*
management.endpoints.web.exposure.exclude=env,configprops

或者,使用YAML格式:

management:
  endpoints:
    web:
      exposure:
        include: '*'
        exclude: env,configprops

6. 使用Jolokia进行远程管理

Jolokia是一个基于HTTP的JMX代理,允许你通过HTTP来访问和管理JMX MBean。如果你需要远程管理你的应用程序,可以使用Jolokia代替Actuator的/jolokia端点(已移除)。

首先,你需要添加Jolokia依赖到你的项目中:

Maven:

<dependency>
    <groupId>org.jolokia</groupId>
    <artifactId>jolokia-core</artifactId>
</dependency>

Gradle:

implementation 'org.jolokia:jolokia-core'

然后,你需要配置Jolokia的访问控制。这可以通过在application.propertiesapplication.yml文件中设置jolokia.config.policyLocation来实现。

例如,创建一个jolokia-access.xml文件:

<?xml version="1.0" encoding="UTF-8"?>
<restrictor>
    <cors>
        <origin>*</origin>
        <methods>GET,HEAD,POST,OPTIONS</methods>
    </cors>

    <commands>
        <command>read</command>
        <command>write</command>
        <command>list</command>
        <command>search</command>
    </commands>

    <mbeans>
        <mbean>
            <name>java.lang:type=Memory</name>
            <attributes>
                <attribute>HeapMemoryUsage</attribute>
                <attribute>NonHeapMemoryUsage</attribute>
            </attributes>
        </mbean>
    </mbeans>

    <clients>
        <client address="127.0.0.1" host="localhost"/>
    </clients>
</restrictor>

然后,在application.properties中配置jolokia.config.policyLocation

jolokia.config.policyLocation=classpath:/jolokia-access.xml

或者,使用YAML格式:

jolokia:
  config:
    policyLocation: classpath:/jolokia-access.xml

注意:

  • Jolokia的访问控制配置文件非常重要,请仔细配置。
  • 建议只允许特定的IP地址或网络访问Jolokia。

7. 使用HTTPS

使用HTTPS加密所有Actuator端点的流量,可以防止中间人攻击。这可以通过配置Spring Boot的HTTPS支持来实现。

首先,你需要生成一个SSL证书。可以使用keytool命令生成一个自签名证书:

keytool -genkeypair -alias selfsigned -keyalg RSA -keystore keystore.jks -validity 3650

然后,你需要配置Spring Boot使用HTTPS。这可以通过在application.propertiesapplication.yml文件中设置server.ssl.key-storeserver.ssl.key-store-passwordserver.ssl.key-alias来实现。

例如:

server.ssl.key-store=keystore.jks
server.ssl.key-store-password=password
server.ssl.key-alias=selfsigned
server.ssl.enabled=true

或者,使用YAML格式:

server:
  ssl:
    key-store: keystore.jks
    key-store-password: password
    key-alias: selfsigned
    enabled: true

注意:

  • 在生产环境中,建议使用由受信任的证书颁发机构(CA)颁发的证书。
  • 配置HTTPS后,所有Actuator端点都只能通过HTTPS访问。

8. 审计日志

启用Actuator的审计日志,可以记录所有对Actuator端点的访问,方便安全分析和问题排查。这可以通过配置management.auditevents.enabled=true来实现。

management.auditevents.enabled=true

或者,使用YAML格式:

management:
  auditevents:
    enabled: true

审计事件将记录在日志中,你可以使用日志分析工具来分析审计事件。

安全检查清单

在部署应用程序之前,请务必检查以下事项:

  • [ ] 禁用不必要的Actuator端点。
  • [ ] 使用Spring Security保护Actuator端点,并配置合适的角色和权限。
  • [ ] 将Actuator端点配置在不同的端口上,并使用防火墙规则限制访问。
  • [ ] 使用IP地址白名单限制对Actuator端点的访问。
  • [ ] 定制端点暴露,只暴露必要的端点。
  • [ ] 使用HTTPS加密所有Actuator端点的流量。
  • [ ] 启用Actuator的审计日志。
  • [ ] 定期审查Actuator的配置,确保安全策略得到有效执行。

Spring Boot Actuator 安全加固要点概括

安全加固Actuator端点至关重要,通过禁用不必要的端点、使用Spring Security进行认证授权、配置自定义端口和IP白名单、细粒度控制端点暴露、启用HTTPS和审计日志,能够有效防止敏感信息泄露和未经授权的访问,保障应用程序的安全。定期审查配置并保持警惕,是维护Actuator安全的关键。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注