[实践总结] 解决日志注入问题的一次实践(log4j2)

问题复现

log4j.xml 配置快照

<?xml version="1.0" encoding="UTF-8"?>
<configuration status="info" monitorInterval="10">
    <properties>
        <Property name="LOG_PATTERN" value="[%d{yyyy-MM-dd HH:mm:ss}] [%-5level] [%thread] [%file:%line] → [%m]%n"/>
    </properties>

    <appenders>
        <console name="CONSOLE" target="SYSTEM_OUT">
            <PatternLayout pattern="${LOG_PATTERN}"/>
            <Filters>
                <ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY"/>
            </Filters>
        </console>
    </appenders>

    <loggers>
        <root level="all">
            <AppenderRef ref="CONSOLE"/>
        </root>
    </loggers>
</configuration>

代码验证:发现日志被注入

String s1 = "Injection1\n\n测试日志注入1";
String s2 = "Injection2\n\r测试日志注入2";
String s3 = "Injection3\n\r\n\r测试日志注入3";
log.info(s1);
System.out.println("---------------上下文分隔符-----------------");
log.info(s2);
System.out.println("---------------上下文分隔符-----------------");
log.info(s3);

//验证结果
[2023-12-05 23:37:41] [INFO ] [main] [Log_Injection.java:12][Injection1

测试日志注入1]
---------------上下文分隔符-----------------
[2023-12-05 23:37:41] [INFO ] [main] [Log_Injection.java:14][Injection2
测试日志注入2]
---------------上下文分隔符-----------------
[2023-12-05 23:37:41] [INFO ] [main] [Log_Injection.java:16][Injection3

测试日志注入3]

在这里插入图片描述

规避方案

方案1:追加净化方法

方案原理:通过追加方法把 CRLF替换掉

public class logUtils {
    /**
     * 获取净化后的消息,过滤掉换行,避免日志注入
     */
    public static String cleanMsg(String message) {
        if (message == null) {
            return "";
        }

        message = message.replace('\n', '_').replace('\r', '_');
        return message;
    }
}

代码验证:\n\r被替换为_,日志不会被注入

String s1 = "Injection1\n\n测试日志注入1";
String s2 = "Injection2\n\r测试日志注入2";
String s3 = "Injection3\n\r\n\r测试日志注入3";
log.info(cleanMsg(s1));
System.out.println("---------------上下文分隔符-----------------");
log.info(cleanMsg(s2));
System.out.println("---------------上下文分隔符-----------------");
log.info(cleanMsg(s3));

//验证结果
[2023-12-05 23:42:52] [INFO ] [main] [Log_Injection.java:20][Injection1__测试日志注入1]
---------------上下文分隔符-----------------
[2023-12-05 23:42:52] [INFO ] [main] [Log_Injection.java:22][Injection2__测试日志注入2]
---------------上下文分隔符-----------------
[2023-12-05 23:42:52] [INFO ] [main] [Log_Injection.java:24][Injection3____测试日志注入3]

优劣:

优:确实会避免日志注入
劣:代码冗余+代码泛滥

在这里插入图片描述

方案2:%m → %enc{%m}{CRLF}

方案原理:

利用Pattern Layout 提供的 enc 标签:
enc可以处理4中格式的转义:{[HTML|XML|JSON|CRLF]},默认进行HTML转义
参考 :log4j2中Pattern Layout 对消息体转义

修改log4f.xml 配置

<Property name="LOG_PATTERN" value="[%d{yyyy-MM-dd HH:mm:ss}] [%-5level] [%thread] [%file:%line] → [%enc{%m}{CRLF}]%n"/>

代码验证:enc 会对 CRLF 进行转义,从而避免日志注入

String s1 = "Injection1\n\n测试日志注入1";
String s2 = "Injection2\n\r测试日志注入2";
String s3 = "Injection3\n\r\n\r测试日志注入3";
log.info(s1);
System.out.println("---------------上下文分隔符-----------------");
log.info(s2);
System.out.println("---------------上下文分隔符-----------------");
log.info(s3);

//验证结果
[2023-12-05 23:47:23] [INFO ] [main] [Log_Injection.java:12][Injection1\n\n测试日志注入1]
---------------上下文分隔符-----------------
[2023-12-05 23:47:23] [INFO ] [main] [Log_Injection.java:14][Injection2\n\r测试日志注入2]
---------------上下文分隔符-----------------
[2023-12-05 23:47:23] [INFO ] [main] [Log_Injection.java:16][Injection3\n\r\n\r测试日志注入3]

优劣:

优:确实会避免日志注入,而且通过修改配置,避免了代码冗余和代码泛滥
劣:日志文件里依旧会有\n\r,如果我们的日志需要被日志可视化服务读取,他们可能会被我们日志注入,这种直观看来感觉就是我们写入日志出问题。

在这里插入图片描述

方案3:%m → %replace{%enc{%m}{CRLF}}{\r|\n|%0D|%0A|%0a|%0d}{}

方案原理:

主体思路:

在上述%enc{%m}{CRLF}转义的基础上,把\n\r给替换为

替换操作语法格式:

replace{pattern}{regex}{substitution}:将 pattern 中匹配 regex 正则的部分替换为 substitution
例如:

  • %replace{%msg}{\s}{} , 删除 msg 中的所有空白
  • %replace{%msg}{\.}{/}, 将logger和msg中的所有点都替换为斜杠
  • %replace{%msg}{\"}{'}, 将msg中的双引号替换为单引号,
  • %replace{%xEx{separator(|)}}{\t}{}用 | 做换行符,并删除其中的 \t 制表符

参考:Apache-Log4j → %replace 替换

CRLF转义后的枚举值列举:

Remove CR and LF characters to prevent CRLF injection

  • location.replaceAll("(\\r|\\n|%0D|%0A|%0a|%0d)", "");

参考:org.owasp.csrfguard.http.InterceptRedirectResponse

修改log4f.xml 配置

<Property name="LOG_PATTERN" value="[%d{yyyy-MM-dd HH:mm:ss}] [%-5level] [%thread] [%file:%line] → [%replace{%enc{%m}{CRLF}}{\\r|\\n|%0D|%0A|%0a|%0d}{}]%n"/>

代码验证:CRLF 被置换为空

String s1 = "Injection1\n\n测试日志注入1";
String s2 = "Injection2\n\r测试日志注入2";
String s3 = "Injection3\n\r\n\r测试日志注入3";
log.info(s1);
System.out.println("---------------上下文分隔符-----------------");
log.info(s2);
System.out.println("---------------上下文分隔符-----------------");
log.info(s3);

//验证结果
[2023-12-05 23:50:48] [INFO ] [main] [Log_Injection.java:12][Injection1测试日志注入1]
---------------上下文分隔符-----------------
[2023-12-05 23:50:48] [INFO ] [main] [Log_Injection.java:14][Injection2测试日志注入2]
---------------上下文分隔符-----------------
[2023-12-05 23:50:48] [INFO ] [main] [Log_Injection.java:16][Injection3测试日志注入3]

优劣:

优:避免了日志注入,且不会对后续服务进行注入
劣:会把\n\r给完全删除了

在这里插入图片描述

参考:Java云服务开发安全问题解析——日志注入,并没那么简单

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
anylog 是一个可以在代码的任意区域无入侵地加入日志的工具,适用于线上问题排查。 anylog 为开发人员提供一个易于使用的平台,帮助开发人员在正在运行的系统中随时加入自己想要的日志,而免于修改代码和重启。 使用场景举例     1、一些同学在写代码时,把异常吃掉了,使得问题难以查找,可以使用这个工具,动态打印出被吃掉         的异常,而不用停机。     2、一些项目依赖第三方jar包,如果发生问题,但第三方包中无日志打印,以往可能需要重新编译第         三方包,加上日志,重启服务,然后排查问题。但使用这个工具,就可以直接动态加入日志,而不用         修改第三方jar包,也不用重启。 已有功能     1、让系统打印某个exception的堆栈,无论此exception是否已经被吃掉都可打印     2、在某个指定类的某个方法的某一行,输出日志。     3、在某个指定类的某个方法的开始,输出日志。     4、在某个指定类的某个方法的结束,输出日志。       5、打印方法耗时,支持方法嵌套。     如果需要扩展新的功能(例如输出jvm的cpu占用,内存大小等),只需要实现spi中的     com.github.jobop.anylog.spi.TransformDescriptor      和com.github.jobop.anylog.spi.TransformHandler接口,     然后把实现的jar包放到providers目录中即可识别。 使用方法     1、获取运行程序:         1)可以到以下地址获取正式发行版:https://github.com/jobop/release/tree/master/anylog         2)你也可以clone下源码后,执行如下命令,生成运行程序,生成的运行程序将在dist目录下             生成windows版本:  mvn install             生成linux版本:  mvn install -Plinux     2、直接执行startup.bat或者startup.sh即可运行起来     3、访问 http://127.0.0.1:52808 即可使用 功能扩展     anylog利用spi机制实现其扩展,如果你想要对anylog增加新的功能(例如添加返回值打印的功能)可以按照如下步骤操作:     1、使用如下命令,生成一个spi实现工程,并导入eclipse     mvn archetype:generate -DarchetypeGroupId=com.github.jobop -DarchetypeArtifactId=anylogspi-archetype -DarchetypeVersion=1.0.4     2、参照该工程中已有的两个例子(一个是在方法开始插入日志,一个是在方法结束插入日志),实现TransformDescriptor和TransformHandler接口     3、把两个接口实现类的全路径,分别加到以下两个文件中         src/main/resources/META-INF/services/com.github.jobop.anylog.spi.TransformDescriptor         src/main/resources/META-INF/services/com.github.jobop.anylog.spi.TransformHandler     4、执行mvn install打包,在dist下会生成你的扩展实现jar。     5、把扩展实现jar拷贝到anylog的providers目录下,重启即可生效。     tips:在实现spi时,我们提供了SpiDesc注解,该注解作用在你实现的TransformDescriptor上,可以用来生成功能描述文字。          如果要深入了解spi机制,请自行google:java spi 标签:anylog
log4j2burpscanner是一个用于检测和防御日志注入攻击的工具。在Web应用程序中,通常会将一些用户输入数据记录到日志文件中,以便后续分析和调试。然而,如果用户能够在输入中注入恶意代码,就可能导致日志文件变得不可信,甚至攻击者可能利用日志注入攻击实施其他攻击。 log4j2burpscanner的作用就是扫描应用程序中的日志记录语句,检测是否存在潜在的日志注入漏洞,并提供相应的防御措施。该工具可以自动识别哪些日志语句是可疑的,并生成报告指出潜在的漏洞点,使开发者能够及时修复这些问题log4j2burpscanner的工作原理是通过分析应用程序中使用的日志框架的配置文件,识别日志记录方法的模式。然后,它会使用一些预定义的规则和正则表达式来检查这些日志语句是否存在潜在的注入漏洞,例如是否将用户输入数据直接拼接到日志语句中,而没有进行适当的过滤和转义。 如果发现可疑的日志语句,log4j2burpscanner会提供一些建议和最佳实践,以帮助开发者修复这些漏洞。例如,建议使用特定的日志方法,如getParametricLog方法,来确保用户输入的数据经过了适当的处理和转义。 总而言之,log4j2burpscanner是一个用于检测和防御日志注入攻击的工具,它可以帮助开发者发现应用程序中的潜在漏洞,并提供相应的修复建议,保障应用程序的安全性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值