信息安全无小事!手把手教你日志脱敏
场景
我们开发的程序迟早有一天都会上线到生产环境运行,但是没有人能保证自己的代码100不出BUG(别抬扛,真没BUG是代码写的少)
当我们线上出BUG之后,最常见的定位问题方法就是排查日志文件,所以我们一般都会在开发程序时,在适当的位置输出一些日志信息。
并且有一些日志并不是只打印一些业务字段,可能会将整个对象输出到日志中。比如这样:log。info(客户信息:{},JSON。toJSONString(customer));
从代码的角度来说,不太严格的话,如果这个customer对象不是特别大的话,倒也没有多大的问题。
但是咱们换个角度,从信息安全的角度来说,这会直接将用户的所有信息都打印在日志中。
假设某一天,开发小哥或者运维小哥因为某些不太建议的操作,恶意从日志中获取用户的信息,比如用户的手机号,邮箱,身份证号,家庭住址如果你们公司是一家金融类公司,直接获取到一些富豪客户的信息,然后去做一些非法交易,后果很严重。
(警告:这是反面教材,万万不可尝试)
所以我们要怎么避免这种问题的发生呢?
可以从以下几个方面解决:避免在代码中将敏感信息直接输出通过日志组件进行脱敏其他我也不知道的方法
在代码中避免的难度相对要更大一点,在一个项目的整个开发周期中很难让所有人都能完全按照规范来执行,所以我们还是要用第二种方式来进行兜底。
接下来的内容主要和大家讲解一下如何通过日志组件进行脱敏。
在Java项目中使用的日志组件一般都会选择Logback或者Log4j,前段时间log4j漏洞风波沸沸扬扬,我们pass它,使用logback来做。
Logback
Logback是Java社区中使用最广泛的日志记录框架之一。它是其Log4j的替代品,比Log4j更快,并提供了更多的配置选项,归档日志文件更灵活。数据脱敏
在我们的系统中多多少少都会有敏感信息,比如身份证号,家庭住址,银行卡号,手机号,邮箱等等,我们需要在记录日志时将这些敏感信息进行脱敏。
假设我们会有如下用户信息日志输出:{userid:123456,address:朝阳区百子湾街道某小区1单元101,city:北京市,Country:中国,mobile:18888888888,email:heiz123xxx。com}
在这个用户信息中,用户的地址,电话,手机,邮箱信息都是比较敏感的,我们需要进行脱敏。
要实现这个功能,我们可以给Logback配置上脱敏规则,将脱敏规则添加到Logback的appender中。我们需要自定义一个Logback的PatternLayout。自定义PatternLayout
我们自定义PatternLayout的目的是为了将脱敏规则添加到logback配置中的所有appender中。packagecom。heiz123。log。layout;importch。qos。logback。classic。PatternLayout;importch。qos。logback。classic。spi。ILoggingEvent;importjava。util。ArrayList;importjava。util。List;importjava。util。regex。Matcher;importjava。util。regex。Pattern;importjava。util。stream。IntStream;authoryuriy。luClassNameMaskingPatternLayoutDescriptiondate2022126publicclassMaskingPatternLayoutextendsPatternLayout{privatePatternmultilinePattern;privatefinalListStringmaskPatternsnewArrayList();publicvoidaddMaskPattern(StringmaskPattern){maskPatterns。add(maskPattern);multilinePatternPattern。compile(String。join(,maskPatterns),Pattern。MULTILINE);}OverridepublicStringdoLayout(ILoggingEventevent){returnmaskMessage(super。doLayout(event));}privateStringmaskMessage(Stringmessage){if(multilinePatternnull){returnmessage;}StringBuildersbnewStringBuilder(message);使用正则匹配符合脱敏要求的数据MatchermatchermultilinePattern。matcher(sb);while(matcher。find()){IntStream。rangeClosed(1,matcher。groupCount())。forEach(group{if(matcher。group(group)!null){将符合格式的数据用替换IntStream。range(matcher。start(group),matcher。end(group))。forEach(isb。setCharAt(i,));}});}returnsb。toString();}}
然后将我们自定义的MaskingPatternLayout配置在Appender中:encoderclassch。qos。logback。core。encoder。LayoutWrappingEncoderlayoutclasscom。heiz123。log。layout。MaskingPatternLayout!自定义layoutmaskPatternaddresss:s(。?)maskPattern!json地址格式maskPattern((1d{2})d{8})maskPattern!手机号格式maskPattern(ww。w)maskPattern!邮箱格式Patternd{HH:mm:ss。SSS}5levellogger{80}msgnPatternlayoutencoderappender
然后我们来测试一下打印结果,使用如下代码模拟用户数据输出:privatestaticvoidtestLog(){MapString,StringusernewHashMap();user。put(userid,123456);user。put(mobile,18888888888);user。put(address,朝阳区百子湾街道某小区1单元101);user。put(city,北京市);user。put(country,中国);user。put(email,heiz123163。com);log。info(customerinfo:{},JSON。toJSONString(user));}
执行后输出结果如下:com。heiz123。study。StudyApplicationcustomerinfo:{country:中国,address:,userid:123456,city:北京市,mobile:,email:}
嗯,达到了数据脱敏的目的。并且我们可以在配置文件中添加不同的正则表达式,对不同格式的信息进行脱敏。实现原理
俗话说要知其然,知其所以然。
为什么在Appender中添加一个自定义Layout就可以做到脱敏呢?我通过阅读源码,给他家整理了一个Logback日志输出的流程图,方便大家理解。
在我们调用log。debug()或者log。info()以及其他级别的日志方法时,会按照上图中的流程执行。
红色字体部分的方法,就会调用到Encoder中的layout的doLayout(Event),也就是我们配置文件中配置的自定义Layout。
最后
以上就是本期的所有内容,主要跟大家讲解如何通过自定义PatternLayout,对logback输出的敏感日志进行脱敏,防止数据泄露。
如果对你有所帮助,希望能给点个赞,就是对我最大的鼓励啦。