<Property name="LOG_PATTERN" value="[%d{yyyy-MM-dd HH:mm:ss}] [%-5level] [%thread] [%file:%line] → [%m]%n"/>
<PatternLayout pattern="${LOG_PATTERN}"/>
log.info("Injection1\n\n测试日志注入1");
System.out.println("处理逻辑1");
log.info("Injection2\r\r测试日志注入2");
System.out.println("处理逻辑2");
log.info("Injection3\n\r\n\r测试日志注入3");
[2023-04-05 08:48:19] [INFO ] [main] [Log_Injection.java:8] → [Injection1
测试日志注入1]
处理逻辑1
测试日志注入2]
处理逻辑2
[2023-04-05 08:48:19] [INFO ] [main] [Log_Injection.java:12] → [Injection3
测试日志注入3]
/**
* 获取净化后的消息,过滤掉换行,避免日志注入
*/
public static String cleanMsg(String message) {
if (message == null) {
return "";
}
message = message.replace('\n', '_').replace('\r', '_');
return message;
}
String s = "Injection1\n\n测试日志注入1";
String s1 = "Injection2\r\r测试日志注入2";
String s2 = "Injection3\n\r\n\r测试日志注入3";
log.info(s);
System.out.println("处理逻辑1");
log.info(s1);
System.out.println("处理逻辑2");
log.info(s2);
System.out.println("--------------------------------");
log.info(cleanMsg(s));
System.out.println("处理逻辑3");
log.info(cleanMsg(s1));
System.out.println("处理逻辑4");
log.info(cleanMsg(s2));
[2023-04-05 09:12:28] [INFO ] [main] [Log_Injection.java:12] → [Injection1
测试日志注入1]
处理逻辑1
测试日志注入2]
处理逻辑2
[2023-04-05 09:12:28] [INFO ] [main] [Log_Injection.java:16] → [Injection3
测试日志注入3]
--------------------------------
[2023-04-05 09:12:28] [INFO ] [main] [Log_Injection.java:20] → [Injection1__测试日志注入1]
处理逻辑3
[2023-04-05 09:12:28] [INFO ] [main] [Log_Injection.java:22] → [Injection2__测试日志注入2]
处理逻辑4
[2023-04-05 09:12:28] [INFO ] [main] [Log_Injection.java:24] → [Injection3____测试日志注入3]
优:确实会避免日志注入
劣:代码冗余+代码泛滥
利用Pattern Layout 提供的标签:
enc
enc
可以处理4中格式的转义:{[HTML
|XML
|JSON
|CRLF
]},默认进行HTML
转义。
参考 :log4j2中Pattern Layout 对消息体转义
<Property name="LOG_PATTERN" value="[%d{yyyy-MM-dd HH:mm:ss}] [%-5level] [%thread] [%file:%line] → [%enc{%m}{CRLF}]%n"/>
<PatternLayout pattern="${LOG_PATTERN}"/>
String s = "Injection1\n\n测试日志注入1";
String s1 = "Injection2\r\r测试日志注入2";
String s2 = "Injection3\n\r\n\r测试日志注入3";
log.info(s);
System.out.println("处理逻辑1");
log.info(s1);
System.out.println("处理逻辑2");
log.info(s2);
System.out.println("--------------------------------");
log.info(cleanMsg(s));
System.out.println("处理逻辑3");
log.info(cleanMsg(s1));
System.out.println("处理逻辑4");
log.info(cleanMsg(s2));
enc 会对 CRLF 进行转义,从而避免日志注入
[2023-04-05 09:26:34] [INFO ] [main] [Log_Injection.java:12] → [Injection1\n\n测试日志注入1]
处理逻辑1
[2023-04-05 09:26:34] [INFO ] [main] [Log_Injection.java:14] → [Injection2\r\r测试日志注入2]
处理逻辑2
[2023-04-05 09:26:34] [INFO ] [main] [Log_Injection.java:16] → [Injection3\n\r\n\r测试日志注入3]
--------------------------------
[2023-04-05 09:26:34] [INFO ] [main] [Log_Injection.java:20] → [Injection1__测试日志注入1]
处理逻辑3
[2023-04-05 09:26:34] [INFO ] [main] [Log_Injection.java:22] → [Injection2__测试日志注入2]
处理逻辑4
[2023-04-05 09:26:34] [INFO ] [main] [Log_Injection.java:24] → [Injection3____测试日志注入3]
优:确实会避免日志注入,而且通过修改配置,避免了代码冗余和代码泛滥
劣:日志文件里依旧会有\n\r,如果我们的日志需要被日志可视化服务读取,他们可能会被我们日志注入,这种直观看来感觉就是我们写入日志出问题。
condition1:
在上述
%enc{%m}{CRLF}
转义的基础上,把\n\r给替换为空
condition2:
replace{pattern}{regex}{substitution}:将 pattern 中匹配 regex 正则的部分替换为 substitution
例如:
- %replace{%msg}{\s}{} , 删除 msg 中的所有空白
- %replace{%logger %msg}{.}{/}, 将logger和msg中的所有点都替换为斜杠
- %replace{%msg}{"}{'}, 将msg中的双引号替换为单引号,
- %replace{%xEx{separator(|)}}{\t}{}用 | 做换行符,并删除其中的 \t 制表符
参考:Apache-Log4j → %replace 替换
condition3:
Remove CR and LF characters to prevent CRLF injection
- location.replaceAll(“(
\\r|\\n|%0D|%0A|%0a|%0d
)”, “”);参考:org.owasp.csrfguard.http.InterceptRedirectResponse
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"/>
<PatternLayout pattern="${LOG_PATTERN}"/>
String s = "Injection1\n\n测试日志注入1";
String s1 = "Injection2\r\r测试日志注入2";
String s2 = "Injection3\n\r\n\r测试日志注入3";
log.info(s);
System.out.println("处理逻辑1");
log.info(s1);
System.out.println("处理逻辑2");
log.info(s2);
System.out.println("--------------------------------");
log.info(cleanMsg(s));
System.out.println("处理逻辑3");
log.info(cleanMsg(s1));
System.out.println("处理逻辑4");
log.info(cleanMsg(s2));
[2023-04-05 10:15:11] [INFO ] [main] [Log_Injection.java:12] → [Injection1测试日志注入1]
处理逻辑1
[2023-04-05 10:15:11] [INFO ] [main] [Log_Injection.java:14] → [Injection2测试日志注入2]
处理逻辑2
[2023-04-05 10:15:11] [INFO ] [main] [Log_Injection.java:16] → [Injection3测试日志注入3]
--------------------------------
[2023-04-05 10:15:11] [INFO ] [main] [Log_Injection.java:20] → [Injection1__测试日志注入1]
处理逻辑3
[2023-04-05 10:15:11] [INFO ] [main] [Log_Injection.java:22] → [Injection2__测试日志注入2]
处理逻辑4
[2023-04-05 10:15:11] [INFO ] [main] [Log_Injection.java:24] → [Injection3____测试日志注入3]
优:避免了日志注入,且不会对后续服务进行注入
劣:会把\n\r给完全删除了
Java云服务开发安全问题解析——日志注入,并没那么简单