Spring Boot Actuator未授权访问:一个注解引发的血案
Spring Boot Actuator未授权访问:一个注解引发的血案
上个月应急响应接到一个case:某企业的Spring Boot应用被入侵了,攻击者通过Actuator端点获取了数据库配置、环境变量中的密钥,最终导致数据泄露。
排查下来原因很简单——开发者图方便,把Actuator全开了,又没做访问控制。
0x01 什么是Actuator?
Spring Boot Actuator是Spring Boot的监控模块,提供了一系列HTTP端点来监控和管理应用:
# application.yml - 危险配置示例
management:
endpoints:
web:
exposure:
include: "*" # 暴露所有端点,极其危险
常用的危险端点:
端点用途风险等级 /actuator/env环境变量高危(泄露密钥) /actuator/configprops配置属性高危(泄露数据库密码) /actuator/heapdump堆转储极高危(含敏感数据) /actuator/mappingsURL映射中危(暴露API结构) /actuator/jolokiaJMX over HTTP极高危(RCE) /actuator/traceHTTP请求记录高危(含Authorization头)0x02 漏洞检测
手动检测很简单:
# 检测Actuator端点是否开放
curl -s http://target:8080/actuator | python3 -m json.tool
# 检测环境变量(可能泄露数据库密码、API Key)
curl -s http://target:8080/actuator/env | grep -i "password\|secret\|key\|token"
# 检测堆转储(最大风险,文件可能几百MB)
curl -s -o heapdump.hprof http://target:8080/actuator/heapdump
# 检测Jolokia(可能实现RCE)
curl -s http://target:8080/actuator/jolokia
批量扫描脚本:
import requests
import sys
import json
from concurrent.futures import ThreadPoolExecutor
DANGEROUS_ENDPOINTS = [
"/actuator",
"/actuator/env",
"/actuator/configprops",
"/actuator/heapdump",
"/actuator/mappings",
"/actuator/jolokia",
"/actuator/trace",
]
def check_actuator(target):
"""检测目标Actuator暴露情况"""
results = []
base_url = f"http://{target}:8080"
for endpoint in DANGEROUS_ENDPOINTS:
try:
resp = requests.get(
f"{base_url}{endpoint}",
timeout=5,
allow_redirects=False
)
if resp.status_code == 200:
# heapdump返回的是二进制,特殊处理
if "heapdump" in endpoint:
results.append(f"[!] {endpoint} - 存在({len(resp.content)} bytes)")
else:
# 尝试解析JSON
try:
data = resp.json()
results.append(f"[!] {endpoint} - 存在")
except json.JSONDecodeError:
results.append(f"[+] {endpoint} - 可能存在(非JSON响应)")
elif resp.status_code == 401:
results.append(f"[-] {endpoint} - 需要认证(已配置安全策略)")
elif resp.status_code == 404:
pass # 端点未暴露,正常
except requests.exceptions.RequestException as e:
pass # 连接失败,跳过
if results:
print(f"\n[!] {target} 发现暴露的Actuator端点:")
for r in results:
print(f" {r}")
else:
print(f"[*] {target} - Actuator端点未暴露或不可达")
def scan_targets(target_file, port=8080):
"""批量扫描"""
with open(target_file, 'r') as f:
targets = [line.strip() for line in f if line.strip()]
print(f"[*] 开始扫描 {len(targets)} 个目标...")
with ThreadPoolExecutor(max_workers=20) as executor:
executor.map(check_actuator, targets)
if __name__ == "__main__":
if len(sys.argv) < 2:
print(f"用法: python3 {sys.argv[0]} <target | target_file>")
print(f"示例: python3 {sys.argv[0]} 192.168.1.100")
print(f"批量: python3 {sys.argv[0]} targets.txt")
sys.exit(1)
target = sys.argv[1]
# 判断是单个目标还是文件
try:
with open(target, 'r') as f:
scan_targets(target)
except FileNotFoundError:
check_actuator(target)
0x03 heapdump敏感信息提取
heapdump是最危险的端点,它导出JVM堆内存,里面可能包含数据库密码、API密钥、用户数据等敏感信息。
用Eclipse MAT或jhat分析:
# 用jhat分析heapdump(JDK自带)
jhat -J-Xmx512m heapdump.hprof
# 访问 http://localhost:7000 查看
# 用strings快速提取敏感信息
strings heapdump.hprof | grep -iE "(password|secret|key|token|jdbc)" | sort -u
Python脚本提取敏感信息:
import re
import sys
def extract_sensitive(filepath):
"""从heapdump中提取敏感信息"""
patterns = {
'password': r'(?i)(password|passwd|pwd)\s*[=:]\s*["\']?([^\s"\'}{]+)',
'secret': r'(?i)(secret|apikey|api_key)\s*[=:]\s*["\']?([^\s"\'}{]+)',
'jdbc': r'jdbc:[a-z]+://[^\s"\'}{]+',
'token': r'(?i)(token|access_token|refresh_token)\s*[=:]\s*["\']?([^\s"\'}{]+)',
}
with open(filepath, 'rb') as f:
content = f.read().decode('utf-8', errors='ignore')
print(f"[*] 正在分析 {filepath}({len(content)} bytes)...\n")
for name, pattern in patterns.items():
matches = re.findall(pattern, content)
if matches:
print(f"[!] 发现 {name.upper()} ({len(matches)} 个):")
for m in set(matches):
if isinstance(m, tuple):
print(f" {m[0]}={m[1]}")
else:
print(f" {m}")
print()
if __name__ == "__main__":
if len(sys.argv) < 2:
print(f"用法: python3 {sys.argv[0]} <heapdump.hprof>")
sys.exit(1)
extract_sensitive(sys.argv[1])
0x04 加固方案
方案一:关闭不必要的端点
# application.yml
management:
endpoints:
web:
exposure:
include: health,info,metrics # 只暴露安全的端点
endpoint:
health:
show-details: when_authorized # 详细健康信息需要认证
方案二:添加Spring Security认证
@Configuration
public class ActuatorSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.requestMatcher(EndpointRequest.toAnyEndpoint())
.authorizeRequests()
.anyRequest().hasRole("ACTUATOR_ADMIN")
.and()
.httpBasic();
}
}
方案三:修改端点路径+IP白名单
# 修改默认路径(增加攻击难度,但不是真正的安全措施)
management:
endpoints:
web:
base-path: /manage-internal
server:
port: 9090 # 使用独立端口,方便做网络隔离
Nginx层做IP限制:
# 只允许内网访问Actuator
location /actuator {
allow 10.0.0.0/8;
allow 172.16.0.0/12;
deny all;
proxy_pass http://backend;
}
0x05 检查清单
给运维同学一个快速自查清单:
- [ ] 生产环境是否暴露了/actuator端点?
- [ ] management.endpoints.web.exposure.include 是否配置了"*"?
- [ ] heapdump端点是否可访问?
- [ ] /actuator/env中是否包含明文密码?
- [ ] Actuator端点是否有访问认证?
- [ ] 是否使用了独立的管理端口?
- [ ] 是否配置了IP白名单?
0x06 经验总结
- 默认配置不等于安全配置。Spring Boot的默认设置偏开发便利,生产环境必须加固
- 监控和安全要平衡。Actuator很有用,但要控制暴露范围
- 敏感信息不要硬编码。用Vault、KMS等密钥管理服务,而不是写在配置文件里
- 网络隔离很重要。管理端口应该只在内网可达,不要暴露到公网
安全加固不需要多高深的技术,把基础的做好,已经能防住90%的攻击。
觉得有用点个赞,有问题评论区交流。关注"安全值班室",持续分享安全运维实战经验。
标签: Spring Boot, Actuator, 安全加固, 运维安全
分类: 安全运维
关注「安全值班室」公众号
每天AI安全早报 + 实战攻防案例 + 网安学习路线连载
浙公网安备 33010602011771号