Apache Log4j2 2.17.0版本插值机制安全改进解析
前言
Apache Log4j2作为Java生态中广泛使用的日志框架,其安全性一直备受关注。本文将深入解析2.17.0版本中对字符串插值机制的关键改进,这些改进主要针对之前版本中存在的安全问题(CVE-2021-45046和CVE-2021-45105)。
安全问题背景
在早期版本中,Log4j2的字符串插值机制允许对不受信任的日志数据进行替换操作,这可能导致恶意用户通过精心构造的输入触发本不应该暴露的代码执行路径。理想情况下,查找操作(Lookups)应该仅由配置和日志框架本身触发,而不应该被用户输入所触发。
核心改进点
1. PatternLayout消息渲染的替换机制
在2.15.0版本中,默认移除了对渲染日志消息内容的替换功能,并在2.16.0版本中完全移除了这一功能。这一改变消除了不受信任输入通过字符串替换系统被评估的最大机会。
2. 查找操作中的递归替换
即使在2.15.0版本中移除了消息替换功能,仍然存在其他途径(基于配置)可以评估不受信任的用户提供值。例如:
- 使用
PatternLayout
模式%p %t %c $${ctx:userAgent} %m%n
时,会因为LiteralPatternConverter
而对每个日志事件重新评估config.getStrSubstitutor().replace(event, " ${ctx:userAgent} ")
- 替换器会递归评估,并触发意外的查找操作
在2.17.0版本中,我们通过一个简单而有效的思路解决了这类问题:
配置解析时允许递归评估(此时没有用户输入/日志事件数据存在,且需要避免配置中断),但在评估日志事件本身时,我们绝不递归评估替换操作。
这意味着:
- 对于
${lower:${event:Message}}
和消息Hello, World!
,结果是hello, world!
- 对于
${java:version}
,结果是字符串字面量${java:version}
,而不会被进一步评估
对现有配置的影响
虽然这些改变确实会影响某些现有配置,但安全性提升带来的好处远大于这些兼容性影响。
兼容性示例分析
正常工作的情况
复杂查找示例:
<PatternLayout pattern="%d %p %t %c $${lower:$${ctx:key1:-$${ctx:key2}}} %m%n"/>
这个例子基于ThreadContext(即MDC)使用复杂查找,它仍然能按预期工作,同时防止用户提供的数据被替换。
通过查找重用模式:
<Properties>
<Property name="defaultPattern">%d %p %t %c %m%n</Property>
</Properties>
<Appenders>
<Console name="console">
<PatternLayout pattern="${defaultPattern}"/>
</Console>
</Appenders>
需要调整的情况
包含延迟查找的延迟模式查找:
<Properties>
<Property name="messageFormat">$${event:Message}</Property>
</Properties>
<Appenders>
<Console name="console">
<PatternLayout pattern="%c $${messageFormat}%n"/>
</Console>
</Appenders>
在2.17.0中,输出会从com.example.Main example
变为com.example.Main ${event:Message}
。
解决方案是取消模式中对属性引用的转义:
<PatternLayout pattern="%c ${messageFormat}%n"/>
RoutingAppender中使用延迟属性引用:
<Properties>
<Property name="filename">target/routing1/routingtest-$${sd:type}.log</Property>
</Properties>
...
<RollingFile name="Routing-${sd:type}" fileName="${filename}"
需要将文件名属性值内联到RollingFile的filename属性中:
<RollingFile name="Routing-${sd:type}" fileName="target/routing1/routingtest-${sd:type}.log"
最佳实践建议
- **优先使用
%X{userAgent}
**而非${ctx:userAgent}
,因为它更安全、更直观且更高效 - 避免在日志消息中使用复杂查找,特别是涉及用户输入的部分
- 简化配置结构,尽量减少依赖递归查找的配置
- 及时升级到2.17.0或更高版本,确保系统安全
总结
Log4j2 2.17.0版本通过限制递归替换操作,显著提高了框架的安全性,同时保持了大多数常见用例的功能。虽然这需要一些配置调整,但这些改变对于保护系统免受潜在风险至关重要。作为开发者,理解这些变化并相应调整配置,将有助于构建更安全的日志系统。
记住,安全性和功能性的平衡是持续的过程,保持对日志框架更新的关注并及时应用安全补丁是维护系统安全的关键步骤。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考