• springboot logback 日志注入安全问题 统一处理


    背景

    日志注入一般指的是恶意用户输出换行等内容,混淆正常的日志,导致排查问题是无法正确定位问题,因此,我们需要对要打印的日志内容进行过滤。
    但是,如果是每个接口单独处理的话,成本较高,因此,我们需要一个统一处理的机制。

    当然,统一处理可能会误伤正常的日志,这个需要自己进行斟酌了。我们在绝大情况下不会在打印日志的内容中使用换行等特殊字符,所以这样处理我认为是比较稳妥的。

    这个处理仅会对用户输入的参数进行处理,不会对堆栈的正常的内容处理,所以大部分的日志是可以正常打印出来的。

    方案

    springboot集成的logback,其配置文件是 org/springframework/boot/logging/logback/base.xml,其默认参数配置都在org/springframework/boot/logging/logback/defaults.xml。我们不打算修改这些配置,而是沿用。

    我们对logback进行定制,spring集成的默认log_pattern为:-%clr(%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd'T'HH:mm:ss.SSSXXX}}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr(%applicationName[%15.15t]){faint} %clr(${LOG_CORRELATION_PATTERN:-}){faint}%clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}},中间可以全部不管,输出的重点就是靠后部分的%m,这个是一个Converter,对消息进行处理,默认是不进行任何处理,直接输出原消息,我们对他进行定制。

    我们自定义自己的Converter:

    
    import ch.qos.logback.classic.pattern.ClassicConverter;
    import ch.qos.logback.classic.spi.ILoggingEvent;
    import org.slf4j.helpers.MessageFormatter;
    
    /**
     * @author 
     */
    public class LineSeparatorConverter extends ClassicConverter {
        @Override
        public String convert(ILoggingEvent event) {
            Object[] argumentArray = event.getArgumentArray();
    
            // 没有用户传入的参数,直接返回
            if (argumentArray == null || argumentArray.length == 0) {
                return event.getFormattedMessage();
            }
    
            Object[] argumentArrayProcessed = new Object[argumentArray.length];
    
            for (int i = 0; i < argumentArray.length; i++) {
                Object o = argumentArray[i];
                if (o == null || o instanceof Throwable) {
                    argumentArrayProcessed[i] = o;
                } else {
                    String oString = o.toString();
                    argumentArrayProcessed[i] = cleanLineSeparator(oString);
                }
            }
            return getFormattedMessage(event.getMessage(), argumentArrayProcessed);
        }
    
        public String getFormattedMessage(String message, Object[] argumentArray) {
            String formattedMessage;
            if (argumentArray != null) {
                formattedMessage = MessageFormatter.arrayFormat(message, argumentArray).getMessage();
            } else {
                formattedMessage = message;
            }
    
            return formattedMessage;
        }
    
        private String cleanLineSeparator(String line) {
            if (line == null || line.isEmpty()) {
                return line;
            }
    
            return line.replace("\n", "\\n").replace("\r", "\\r");
        }
    
    }
    
    

    然后,声明logback-spring.xml,使用spring的默认配置,并且覆盖%m的默认Converter,改成我们自己的

    <?xml version="1.0" encoding="UTF-8"?>
    
    <configuration scan="true" scanPeriod="60 seconds" debug="false">
    
        <include resource="org/springframework/boot/logging/logback/base.xml"/>
        <!-- 将默认的输出器改为自定义输出器,会替换掉换行符,防止日志注入 -->
        <conversionRule conversionWord="m"
                        converterClass="xxx.LineSeparatorConverter"/>
    
    </configuration>
    
    

    如果放到了resources目录下,该文件应该可以正常识别到,不需要额外配置。
    如果日志处理是一个maven的子模块,那就手动声明一下文件位置
    logging.config=classpath:logback-spring.xml
    如果放到其他目录,写成:logging.config=file:/xxx/xxx/logback-spring.xml

  • 相关阅读:
    云时代全光底座架构及关键技术
    LeetCode530.二叉搜索树的最小绝对差 501二叉搜索树中的众数 236二叉树的最近公共祖先
    SoildNet: Soiling Degradation Detection inAutonomous Driving 个人理解
    论文阅读 - Outlier detection in social networks leveraging community structure
    Redis中的数据类型
    python 全网最优雅命令行参数解析, 没有之一
    SQL每日一练(牛客新题库)——第7天:必会常用函数
    SpringBoot + uniApp实现的掌上生鲜超市购物微信小程序系统 附带详细运行指导视频
    《俞军产品方法论》笔记心得 03
    110-注解JSONField、DateTimeFormat、JsonFormat、JsonProperty
  • 原文地址:https://blog.csdn.net/weixin_44670774/article/details/138853640