news 2026/6/10 19:14:44

生产环境日志脱敏方案:保护用户隐私数据

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
生产环境日志脱敏方案:保护用户隐私数据

被安全部门约谈了一次。

原因是日志里明文打印了用户手机号、身份证号,还被导出到了测试环境。这要是出了事,GDPR罚款能让公司破产。

花了两周时间做日志脱敏,整理一下方案。

为什么要日志脱敏

日志里经常会有:

  • 手机号、身份证号
  • 银行卡号、密码(别笑,真有人打印)
  • 地址、邮箱
  • Token、API Key

这些数据如果:

  • 被运维人员看到 → 内部泄露风险
  • 日志被导出 → 外部泄露风险
  • 日志被攻击者获取 → 直接完蛋

方案一:代码层脱敏

最可控的方式,在打日志的地方处理。

1.1 自定义toString

publicclassUser{privateStringname;privateStringphone;privateStringidCard;@OverridepublicStringtoString(){return"User{"+"name='"+maskName(name)+'\''+", phone='"+maskPhone(phone)+'\''+", idCard='"+maskIdCard(idCard)+'\''+'}';}privateStringmaskPhone(Stringphone){if(phone==null||phone.length()!=11)returnphone;returnphone.substring(0,3)+"****"+phone.substring(7);}privateStringmaskIdCard(StringidCard){if(idCard==null||idCard.length()<10)returnidCard;returnidCard.substring(0,4)+"**********"+idCard.substring(idCard.length()-4);}privateStringmaskName(Stringname){if(name==null||name.length()<2)returnname;returnname.charAt(0)+"*".repeat(name.length()-1);}}

打印出来:

User{name='张*', phone='138****5678', idCard='3201**********1234'}

1.2 注解方式

更优雅一点,用注解标记敏感字段:

// 自定义注解@Target(ElementType.FIELD)@Retention(RetentionPolicy.RUNTIME)public@interfaceSensitive{SensitiveTypetype();}publicenumSensitiveType{PHONE,ID_CARD,NAME,BANK_CARD,EMAIL}// 使用注解publicclassUser{@Sensitive(type=SensitiveType.NAME)privateStringname;@Sensitive(type=SensitiveType.PHONE)privateStringphone;@Sensitive(type=SensitiveType.ID_CARD)privateStringidCard;}
// 脱敏工具类publicclassSensitiveUtil{publicstaticStringmask(Objectobj){if(obj==null)return"null";Class<?>clazz=obj.getClass();StringBuildersb=newStringBuilder(clazz.getSimpleName()+"{");Field[]fields=clazz.getDeclaredFields();for(inti=0;i<fields.length;i++){Fieldfield=fields[i];field.setAccessible(true);Stringvalue;try{ObjectfieldValue=field.get(obj);if(field.isAnnotationPresent(Sensitive.class)){Sensitiveannotation=field.getAnnotation(Sensitive.class);value=doMask(String.valueOf(fieldValue),annotation.type());}else{value=String.valueOf(fieldValue);}}catch(IllegalAccessExceptione){value="N/A";}sb.append(field.getName()).append("='").append(value).append("'");if(i<fields.length-1)sb.append(", ");}returnsb.append("}").toString();}privatestaticStringdoMask(Stringvalue,SensitiveTypetype){if(value==null||"null".equals(value))returnvalue;switch(type){casePHONE:returnvalue.length()==11?value.substring(0,3)+"****"+value.substring(7):value;caseID_CARD:returnvalue.length()>=10?value.substring(0,4)+"**********"+value.substring(value.length()-4):value;caseNAME:returnvalue.length()>=2?value.charAt(0)+"*".repeat(value.length()-1):value;caseBANK_CARD:returnvalue.length()>=8?value.substring(0,4)+"****"+value.substring(value.length()-4):value;caseEMAIL:intatIndex=value.indexOf("@");returnatIndex>2?value.substring(0,2)+"***"+value.substring(atIndex):value;default:returnvalue;}}}

使用:

log.info("用户信息: {}",SensitiveUtil.mask(user));// 输出:用户信息: User{name='张*', phone='138****5678', idCard='3201**********1234'}

方案二:日志框架层脱敏

在Logback/Log4j2层面统一处理。

2.1 Logback自定义Converter

publicclassSensitivePatternConverterextendsClassicConverter{privatestaticfinalPatternPHONE_PATTERN=Pattern.compile("1[3-9]\\d{9}");privatestaticfinalPatternIDCARD_PATTERN=Pattern.compile("\\d{17}[\\dXx]");privatestaticfinalPatternEMAIL_PATTERN=Pattern.compile("[\\w.]+@[\\w.]+");@OverridepublicStringconvert(ILoggingEventevent){Stringmessage=event.getFormattedMessage();// 手机号脱敏message=PHONE_PATTERN.matcher(message).replaceAll(m->m.group().substring(0,3)+"****"+m.group().substring(7));// 身份证脱敏message=IDCARD_PATTERN.matcher(message).replaceAll(m->m.group().substring(0,4)+"**********"+m.group().substring(14));// 邮箱脱敏message=EMAIL_PATTERN.matcher(message).replaceAll(m->{Stringemail=m.group();intatIndex=email.indexOf("@");returnatIndex>2?email.substring(0,2)+"***"+email.substring(atIndex):email;});returnmessage;}}
<!-- logback.xml --><configuration><conversionRuleconversionWord="sensMsg"converterClass="com.example.SensitivePatternConverter"/><appendername="CONSOLE"class="ch.qos.logback.core.ConsoleAppender"><encoder><pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger - %sensMsg%n</pattern></encoder></appender></configuration>

这样所有日志自动脱敏,无需改业务代码。

2.2 Log4j2 RewritePolicy

@Plugin(name="SensitiveRewritePolicy",category="Core",elementType="rewritePolicy")publicclassSensitiveRewritePolicyimplementsRewritePolicy{@OverridepublicLogEventrewrite(LogEventevent){Messagemessage=event.getMessage();StringformattedMessage=message.getFormattedMessage();// 脱敏处理StringmaskedMessage=maskSensitiveData(formattedMessage);returnnewLog4jLogEvent.Builder(event).setMessage(newSimpleMessage(maskedMessage)).build();}privateStringmaskSensitiveData(Stringmessage){// 同上,正则替换returnmessage;}}

方案三:日志采集层脱敏

在Logstash/Filebeat采集时处理。

3.1 Logstash Filter

filter{# 手机号脱敏mutate{gsub=>["message","1[3-9]\d{9}","1**********"]}# 身份证脱敏mutate{gsub=>["message","\d{17}[\dXx]","****"]}# 银行卡脱敏mutate{gsub=>["message","\d{16,19}","****"]}}

3.2 Filebeat Processor

processors:-script:lang:javascriptsource:>function process(event) { var message = event.Get("message"); // 手机号脱敏 message = message.replace(/1[3-9]\d{9}/g, function(match) { return match.substring(0,3) + "****" + match.substring(7); }); event.Put("message", message); }

方案四:落盘后处理

如果历史日志已经有敏感信息,需要清洗。

#!/bin/bash# 日志脱敏脚本LOG_DIR="/var/log/app"# 手机号脱敏find$LOG_DIR-name"*.log"-execsed-i -E's/1([3-9])[0-9]{9}/1\1*******/g'{}\;# 身份证脱敏find$LOG_DIR-name"*.log"-execsed-i -E's/[0-9]{6}[0-9]{8}[0-9]{3}[0-9Xx]/******/g'{}\;

最佳实践

分层防护

代码层 → 日志框架层 → 采集层 → 存储层 ↓ ↓ ↓ ↓ 不打印敏感 自动脱敏 二次脱敏 加密存储

建议至少做两层。

敏感数据分类

级别数据类型处理方式
L1绝密密码、密钥禁止打印
L2机密身份证、银行卡强脱敏
L3敏感手机号、邮箱弱脱敏
L4普通姓名、地址可脱敏可不脱敏

禁止打印的内容

// 密码绝对不能打印log.info("用户登录: {}, 密码: {}",username,password);// 错误// Token不能打印log.info("Token: {}",token);// 错误// API Key不能打印log.info("调用第三方API, key={}",apiKey);// 错误

正确做法:

// 只打印是否存在log.info("用户登录: {}, 密码: {}",username,password!=null?"[已设置]":"[未设置]");// Token只打印部分log.info("Token: {}...",token.substring(0,8));

验证脱敏效果

写个测试用例:

@TestpublicvoidtestSensitiveMask(){Useruser=newUser();user.setName("张三");user.setPhone("13812345678");user.setIdCard("320123199001011234");Stringmasked=SensitiveUtil.mask(user);// 验证脱敏后不包含原始数据assertFalse(masked.contains("13812345678"));assertFalse(masked.contains("320123199001011234"));// 验证脱敏格式正确assertTrue(masked.contains("138****5678"));assertTrue(masked.contains("3201**********1234"));}

日志安全审计

除了脱敏,还要控制日志访问权限。

我们有几台日志服务器分布在不同机房,之前权限管理很乱。现在用星空组网把所有节点连起来后,统一通过跳板机访问,权限管控清晰多了。

总结

日志脱敏方案选择:

方案优点缺点推荐场景
代码层最可控需要改代码新项目
日志框架层全局生效性能有损耗老项目改造
采集层不改代码本地日志仍有敏感信息补充方案
落盘后处理处理历史数据不实时应急处理

核心原则:

  1. 能不打印的就不打印
  2. 必须打印的要脱敏
  3. 脱敏要分级
  4. 多层防护

日志安全这块有经验的欢迎交流~

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/10 12:46:08

手把手教你制作Arduino寻迹小车(新手教程)

从零开始做一辆会“认路”的小车&#xff1a;Arduino寻迹实战全记录你有没有想过&#xff0c;让一个小车自己沿着黑线跑&#xff0c;不用遥控、也不靠人推&#xff1f;这听起来像是机器人比赛里的高科技项目&#xff0c;其实——用一块Arduino板子、几个红外传感器和电机驱动模…

作者头像 李华
网站建设 2026/6/10 12:29:50

盲盒一番赏小程序:核心功能与玩法全解析

在盲盒经济与小程序生态深度融合的当下&#xff0c;一番赏凭借“梯度奖项确定性惊喜”的核心魅力&#xff0c;成为潮玩、IP衍生品领域的流量密码。盲盒一番赏小程序通过轻量化形态降低用户参与门槛&#xff0c;同时以完善的功能体系和创新玩法构建商业闭环。本文将从核心功能、…

作者头像 李华
网站建设 2026/6/10 12:42:16

EasyGBS视频监控助力实现建筑工地安全监管智能化

在建筑行业快速发展的今天&#xff0c;工地安全管理始终是项目管理的重中之重。而视频监控作为核心感知手段&#xff0c;承担着安全监管、流程规范、风险预警的关键作用。不同于普通场景&#xff0c;建筑工地存在人员流动大、作业环境复杂、高危环节多、跨部门协同需求强等痛点…

作者头像 李华
网站建设 2026/6/10 12:24:25

不懂SGLang?从miniSGLang开始,轻松入门编程新世界!

SGL社区中推出一款轻量的推理框架&#xff1a;miniSGLang[1]&#xff0c;支持完整的LLM推理。用约5千行Python代码和少量c代码实现&#xff0c;麻雀虽小五脏俱全。相比SGLang上十万行的代码&#xff0c;miniSGLang简单易读&#xff0c;非常适合用于相关概念的理解。 本文结合&…

作者头像 李华
网站建设 2026/6/10 12:32:30

从信息检索到智能突破:大模型的bad case解决方案与在线策略蒸馏!

简介 文章探讨了当前大模型面临的bad case问题&#xff0c;指出若不解决底层神经网络问题&#xff0c;大模型将沦为信息检索工具。文章介绍了在线策略蒸馏技术&#xff0c;这是一种结合了强化学习(在线策略)和蒸馏(密集奖励信号)的创新方法。它从学生模型采样轨迹&#xff0c;…

作者头像 李华
网站建设 2026/6/10 14:10:16

从零上手:用AI智能体实现微信自动回复功能全攻略

在日常工作与生活中&#xff0c;微信早已成为核心沟通工具&#xff0c;但频繁的重复咨询、夜间消息轰炸常常让人分身乏术。借助AI智能体实现微信自动回复&#xff0c;既能实现724小时不间断响应&#xff0c;又能精准解答标准化问题&#xff0c;大幅解放人力。本文将针对不同技术…

作者头像 李华