日志里的敏感信息还在打明文?3 种日志脱敏方案任你选

关注公众号【1024个为什么】,及时接收最新推送文章!

下面链接文章中重新做了梳理,补充了基于 log4j  的解决方案,建议大家阅读最新文章。

《一次性解决打日志时的4个重复低效场景(日志脱敏、日期格式化、json序列化)》

背景

我们打的日志中经常包含姓名、手机号、银行卡号等敏感信息,如果不做任何处理,就会以明文的形式展示在日志中,存在安全风险。

像下面这样:

5d99b4681b7fd2aa7fe9551775c082b5.png

我们需要一种能自动帮我们脱敏的工具,效果如下:

39f27124e109403843abab637db8b451.png

方案1 - 基于 logback

我们得先搞清楚消息内容是在哪里处理的,也就是配置文件中这个占位符的内容:

191fd9b15563920a8a693bc21329da68.png

对应到源码是这里 ch.qos.logback.classic.PatternLayout :

3519b2a3dd9ec032143a890d3b1bc8fa.png

这里可以看出来都是 通过这个类处理的  ch.qos.logback.classic.pattern.MessageConverter。

继续看一下这个类的逻辑:

public class MessageConverter extends ClassicConverter {
    public MessageConverter() {
    }


    public String convert(ILoggingEvent event) {
        return event.getFormattedMessage();
    }
}

只有一个 convert 方法,想要修改日志的内容,重载此方法即可。

public class TuoMinConverter extends MessageConverter {
    @Override
    public String convert(ILoggingEvent event){
        try {
            String msg = event.getFormattedMessage();
            // 这里自定义脱敏的逻辑
            return doTuoMin(msg);
        }catch (Exception e){
            return super.convert(event);
        }
    }
}

可以参考这位朋友的写法:

https://blog.csdn.net/u011277745/article/details/108793590

再从 logback.xml 中加上这行配置

<conversionRule conversionWord="msg" converterClass="com.TuoMinConverter "/>

两个属性说明一下:

conversionWord:不能乱配,要和图里红框内的内容一致。

f88bc8c3ac491141ce87623781770bfc.png

converterClass:就配我们刚写的转换类 TuoMinConverter 。

缺点:

这种方案处理的是整条日志内容,一般是通过正则匹配再替换内容,不能精准替换指定属性的内容,还存在误杀情况。

59b02cbb2d522c7baf5bc1e3e17eee01.png

这个例子中的 [country : 中国] 就被误杀。

只要满足我们定义的正则,就会被误杀,就会出现很多不该被脱敏的也脱敏了的情况。

方案2 - 基于 fastjson

思路是把要打印的内容脱敏后,再交给日志框架。

这里又回到上篇文章里《日志里打出来的都是时间戳?教你一行代码搞定它》说的 JSON.toJSONString()。

fastjson 提供了很多 Filter,我们这里的场景是在序列化成字符串的时候替换掉 json 键值对里 value 的值,所以要用到这个 Filter。

com.alibaba.fastjson.serializer.ValueFilter
public interface ValueFilter extends SerializeFilter {
    Object process(Object object, String name, Object value);
}

介绍一下 process 方法的 3 个参数:

object:本次用不到,不用关注

name:可以理解为当前处理 java 对象的属性名,比如只处理手机号(phone)的话就可以根据 name 过滤

if(name.equals("phone")){
    value = 脱敏逻辑(value);
}

value:属性对应的值,也就是我们真正要脱敏的内容

实现这个接口,在 process 方法中编写脱敏逻辑。

public class DataMaskFilter implements ValueFilter {


    public static DataMaskFilter instance(){
        return new DataMaskFilter();
    }


    @Override
    public Object process(Object object, String name, Object value) {
        for (DataMaskRuleEnum rule : DataMaskRuleEnum.values()) {
            List<String> fields = Arrays.asList(rule.fieldName.split("\\|"));
            if(fields.contains(name.toUpperCase())){
                value = value.toString().replaceAll(rule.regular, rule.result);
            }
        }
        return value;
    }
}

这里我把脱敏规则单独放到了一个枚举类中,因为在实际工作中,可能 phone, phoneNo, phoneNum  都表示手机号,抽到枚举中单独管理方便添加新的属性名。只要属性名在枚举配置里能找到,就对它脱敏。

public enum DataMaskRuleEnum {
    ID_CARD("身份证号脱敏", "IDCARD", "^(\\d{4})\\d+(\\d{4})$", "$1****$2"),
    PHONE("手机号脱敏", "PHONE|BANKPHONE", "^(\\d{3})\\d+(\\d{4})$", "$1****$2"),
    BANK_CARD("银行卡号脱敏", "CARDNUM", "^(\\d{4})\\d+(\\d{4})$", "$1****$2"),
    NAME("姓名脱敏", "NAME|REALNAME|WORKERNAME", "^([\\u4E00-\\u9FA5]{1,3})([\\u4E00-\\u9FA5])$", "**$2")
    ;


    DataMaskRuleEnum(String description, String fieldName, String regular, String result){
        this.description = description;
        this.fieldName = fieldName;
        this.regular = regular;
        this.result = result;
    }


    /**
     * 脱敏规则描述
     */
    public String description;
    /**
     * 要脱敏的属性名
     */
    public String fieldName;
    /**
     * 脱敏规则
     */
    public String regular;
    /**
     * 脱敏结果
     */
    public String result;
}

使用:

logger.info("保存银行卡操作日志信息, cardLogDto={}", 
    JSON.toJSONString(cardLogDto, DataMaskFilter.instance()));

优点:

可以精准脱敏,具体到指定的属性名,基本不存在误杀情况(除非属性名起的很怪)。

缺点:

每次打日志的时候都要考虑是否传 SerializeFilter 这个参数。

方案3 - 基于 logback + fastjson

这个方案是集前两个方案的优点于一身。

思路是把 fastjson 的脱敏,嵌套在 logback 的 MessageConverter 中。

public class TuoMinConverter extends MessageConverter {
    @Override
    public String convert(ILoggingEvent event){
        try {
            return doTuoMin(event);
        }catch (Exception e){
            return super.convert(event);
        }
    }


    private String doTuoMin(ILoggingEvent event){
        try {
            Object[] objects = Stream.of(event.getArgumentArray()).map(obj -> {
                String msg;
                if (obj instanceof String) {
                    // String 类型直接打印
                    msg = obj.toString();
                } else {
                    // 其他类型 通过 fastjson Filter 功能脱敏后转成 json 字符串
                    msg = JSON.toJSONString(obj);
                }
                return msg;
            }).toArray();
            return MessageFormatter.arrayFormat(event.getMessage(), objects).getMessage();
        } catch (Exception e) {
            return event.getMessage();
        }
    }
}

两个地方重点说明一下:

event.getArgumentArray() 是获取到 logger.info() 方法中的所有参数,获取到参数之后,遍历每个参数,String 类型的直接打印,其他类型的变成 json 字符串后返回。得到的 objects 数组就是经过脱敏处理的  json 字符串对象集合。

MessageFormatter.arrayFormat(event.getMessage(), objects) 是把脱敏处理过的内容填充到日志的占位符中。

额外说明一下:

细心的朋友可能已经发现脱敏处理就一行  msg = JSON.toJSONString(obj);  怎么就脱敏了???

玄机还是在上篇文章《日志里打出来的都是时间戳?教你一行代码搞定它》里提到的 SerializeConfig  全局配置,这个里面不仅可以添加 ObjectSerializer 还可以添加 SerializeFilter 。

static {
    SerializeConfig.getGlobalInstance().put(Date.class, new SimpleDateFormatSerializer("yyyy-MM-dd HH:mm:ss"));
    SerializeConfig.getGlobalInstance().addFilter(User.class, DataMaskFilter.instance());
    SerializeConfig.getGlobalInstance().addFilter(UserAccountDto.class, DataMaskFilter.instance());
}

可以为指定的 java 类型添加指定的 SerializeFilter ,需要为哪些 java 对象脱敏,加到这里就行,做到了更加精准的脱敏。

通过全局配置,toJSONString(obj)  就不用再传其他参数了,也支持集合。

看看效果

918eac86c37a13f3168e936f261ace1b.png

是不是很过瘾,以后就连日志参数都不用每次加一层 JSON.toJSONString()了,随意往里面扔各种对象。

缺点:

这么完美的方案哪还有缺点。

优点:

时间戳、脱敏 一站式解决,好不好用一看就知道。

扯两句

只要是重复的代码,就能往外抽

以后日志就这么打,让他们好奇去吧

原创不易,多多关注,一键三连,感谢支持!

参考文献:

https://blog.csdn.net/weixin_43897590/article/details/115729271

https://blog.csdn.net/u011277745/article/details/108793590

<think>好的,用户想安全地删除或清理日志文件中的敏感信息。首先,我需要回忆一下相关的引用内容,看看有哪些方法可以应用。 根据引用[1],Git中可以使用`git filter-branch`或者BFG工具来清除历史记录中的敏感文件。比如用`git rm --cached`命令移除文件,然后用filter-branch重写历史。引用[3]提到用BFG的`--delete-files`选项删除指定文件,这可能更高效。对于Git用户来说,这些方法很重要,因为直接删除文件并不能从历史中清除,必须重写提交记录。 然后是系统日志的处理。引用[2]讲到了Oracle监听日志的清理,通过停止监听服务,删除旧的日志文件,再重启服务。这说明对于正在运行的日志文件,可能需要先停止相关服务才能安全删除,否则文件可能被占用,或者数据未完全写入。类似的情况可能出现在其他服务如Apache、Nginx的日志处理中,所以通用的步骤应该是停止服务、备份并删除日志、再启动服务。 引用[4]讨论了日志库的管理策略,比如glog、log4cplus和spdlog的日志轮转和清理机制。这提到应用层应该配置自动清理策略,比如按时间或大小滚动日志,并设置保留期限。这可能帮助用户预防敏感信息被长期留存,属于预防措施的一部分。 接下来需要综合这些方法,给出系统化的步骤。首先建议立即清理现有日志,包括识别敏感信息的位置,停止相关服务,使用工具或命令删除或覆盖敏感内容,并处理版本控制中的历史记录。然后,长期措施包括配置日志轮转、过滤敏感信息、访问控制、加密和监控。 需要注意不同场景下的处理方式,比如版本控制系统和服务日志的区别。Git需要重写历史,而应用日志可能需要服务重启。还要提醒用户备份和权限问题,避免误删或权限不足导致操作失败。此外,自动化工具如logrotate可以简化日志管理,减少人为错误。 用户可能需要具体的命令示例,比如使用sed替换日志中的敏感数据,或者用logrotate配置。同时,要强调测试备份的重要性,确保清理操作不会影响系统运行。最后,提醒定期审查和更新策略,以应对新的安全需求。</think>### 安全清理日志文件中敏感信息的步骤 #### 一、立即清理现有日志文件 1. **定位敏感信息** 使用`grep`或文本编辑器全局搜索日志文件中的敏感关键词(如密码、密钥、身份证号),例如: ```bash grep -rnw &#39;/var/log/&#39; -e &#39;password&#39; ``` 2. **覆盖或删除敏感内容** - **直接删除文件**(适用于非持续写入的日志): 停止相关服务后删除文件(如Oracle监听日志)[^2]: ```bash lsnrctl stop # 停止监听服务 rm -rf listener.log.11 # 删除历史日志文件 lsnrctl start # 重启服务 ``` - **清空正在写入的日志**: 使用`truncate`或重定向清空文件(保留文件句柄): ```bash truncate -s 0 /var/log/nginx/access.log ``` 3. **版本控制系统清理**(如Git历史记录) - 使用`git filter-branch`或BFG工具删除历史提交中的敏感文件[^1][^3]: ```bash # 使用BFG删除特定文件 java -jar bfg.jar --delete-files password.txt my-repo.git git reflog expire --expire=now --all && git gc --prune=now --aggressive ``` --- #### 二、长期防护策略 1. **配置日志轮转与自动清理** - 使用`logrotate`工具(Linux系统)配置日志保留策略,例如保留7天并压缩旧文件: ```conf /var/log/app/*.log { daily rotate 7 compress missingok notifempty } ``` - 日志库内置策略(如glog/spdlog): 在代码中设置日志文件大小上限和保留数量[^4]。 2. **敏感信息过滤** - **应用层过滤**:在日志输出前使用正则表达式脱敏(例如替换密码为`***`): ```python import re log_content = re.sub(r&#39;(password=)(\w+)&#39;, r&#39;\1***&#39;, raw_content) ``` - **系统层过滤**:通过`sed`批量清理已存在的敏感数据: ```bash sed -i &#39;s/credit_card=[0-9]\+/credit_card=****/g&#39; /var/log/payment.log ``` 3. **访问控制与加密** - 限制日志文件权限: ```bash chmod 640 /var/log/sensitive.log # 仅允许root和组用户读取 chattr +a /var/log/audit.log # 防止文件被意外修改(Linux扩展属性) ``` - 启用日志文件加密(如LUKS磁盘加密或应用级加密工具)。 --- #### 三、注意事项 1. **操作前备份** ```bash cp -a /var/log/app.log /backup/app.log.bak # 保留原始数据以便恢复 ``` 2. **验证清理效果** 使用`diff`工具对比清理前后差异,或通过`strings`命令检查二进制日志中是否残留明文3. **审计与监控** 部署日志审计系统(如ELK Stack)实时检测敏感信息泄露,并设置告警规则。 ---
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值