介紹
本文是一次數(shù)據(jù)泄漏之后的一點兒思考,系統(tǒng)日志對于后端系統(tǒng)而言是非常重要的丈积,但是大多數(shù)開發(fā)人員在打印日志時筐骇,是非常隨意的,不會去想太多江滨,覺得日志打印的越多铛纬,排查總是越是方便。但是一些關(guān)鍵信息在打印日志時一定要注意唬滑,如果這些信息被人利用告唆,可能會存在很大的風險。
下面我舉例幾個場景晶密,想想這樣的場景中存在的風險
1擒悬、用戶名、密碼稻艰、交易密碼等敏感信息懂牧,如果這些信息明文打印在日志,這些信息被人拿到之后尊勿,就存在很大的安全隱患归苍,這種隱患可能會直接導(dǎo)致用戶的損失,影響公司的形象运怖。
2拼弃、薪資系統(tǒng)中,姓名摇展、薪資等敏感信息吻氧,在每次列表、詳情查詢的時候不加考慮全部打印日志,只要有日志查看權(quán)限的人員中就可能查看到大部分人的薪資情況盯孙,導(dǎo)致公司薪資信息泄漏鲁森,可能會導(dǎo)致同一團隊的人員的薪資泄漏,造成人員流失等
其實對于這些場景振惰,我們只要在日志打印時歌溉,進行脫敏處理就可以,最近公司存在一起數(shù)據(jù)泄漏的事件骑晶,在這個事件之后痛垛,公司也比較重視這一塊,BOSS也因此很不開心桶蛔,在此事之后匙头,下班之余看了自己擴寫了一個日志脫敏的工具,因為公司內(nèi)部打印日志統(tǒng)一用的logback仔雷,本次擴展也是針對logback進行擴展
主要思路
每個團隊甚至每個人打印日志的習(xí)慣和風格都不一樣蹂析,基于日志格式輸出的多樣性,
所以本人覺得最好的辦法是通過配置脫敏規(guī)則碟婆,然后在日志輸出之前進行脫敏處理
整理了一下我們系統(tǒng)中日志打印的風格电抚,大數(shù)數(shù)是以下三種
- log.info("xxxxx: {} " , JSONObject.toJsonString(result));
- log.info("xxxxx: {} " , result );
- log.info("xxxxxx: " + result );
實現(xiàn)過程
第一步:針對上述日志整理,我們先定義一下自己的脫敏規(guī)則竖共,增加配置文件logback-desensitization-rule.properties蝙叛,配置如下
這里需要一些正則的知識,大家請自行補一下
#JSON字段中的mobile , telphone關(guān)鍵字對應(yīng)的內(nèi)容進行脫敏
RULE_REG_1=(\"mobile\"|\"telphone\")(:\")(\\w{3})(\\w{4})(\\w{4})*(\")&$1$2$3****$5$6
#JSON字段中的"證件號碼"關(guān)鍵字對應(yīng)的內(nèi)容進行脫敏
RULE_REG_2=(\"idcard\")(:\")(\\w{2})(\\w{1,})(\\w{2})(\")&$1$2$3*********$5$6
#JSON字段中的"密碼"對應(yīng)的內(nèi)容進行全部*顯示
RULE_REG_3=(\"password\")(:\")(\\w+)(\")&$1$2*****$4
#JSON字段中的"用戶名"對應(yīng)的內(nèi)容除第一位肘迎,其它位脫敏顯示
RULE_REG_4=(\"customerName\"|\"userName\"|\"name\")(:\")([\u4E00-\u9FA5]{1})[\u4E00-\u9FA5]{1,}(\")&$1$2$3**$4
#log.info("password:{}",password);類似這樣的日志甥温,關(guān)鍵字后的8位中,后五位脫敏顯示
RULE_REG_5=(password|mobile)([:|=|,| ]+)(\\w{3})(\\w{5})&$1$2$3*****
第二步:繼承MessageConverter實現(xiàn)日志脫敏
package com.bk.framework.extension.logback;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.pattern.MessageConverter;
import ch.qos.logback.classic.spi.ILoggingEvent;
import com.google.common.collect.Lists;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.slf4j.LoggerFactory;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
/**
* @author BK
* @version V2.0
* @description: logback擴展消息轉(zhuǎn)換器 支持正則脫敏
* @date 2019/6/1 21:23
*/
@Slf4j
public class DesensitizationMessageConverter extends MessageConverter {
private static volatile List<RuleConfig> configList;
@Override
public String convert(ILoggingEvent event) {
initRuleConfig();
return doConvert(event);
}
/**
* 初始化規(guī)則配置
*/
private void initRuleConfig() {
if (configList == null) {
synchronized (DesensitizationMessageConverter.class) {
if (configList == null) {
configList = Lists.newArrayList();
Map<String, String> propertyMap = ((LoggerContext) LoggerFactory.getILoggerFactory()).getCopyOfPropertyMap();
for (String s : propertyMap.keySet()) {
if (s.startsWith("RULE_REG_")) {
String[] array = propertyMap.get(s).split("&");
if (ArrayUtils.isNotEmpty(array) && array.length == 2) {
configList.add(new RuleConfig(array[0], array[1]));
}
}
}
log.info("desensitization rule config init end ! ");
}
}
}
}
/**
* 日志內(nèi)容轉(zhuǎn)換
*
* @param event
* @return
*/
private String doConvert(ILoggingEvent event) {
String result = event.getFormattedMessage();
if (configList != null) {
for (RuleConfig ruleConfig : configList) {
result = ruleConfig.apply(result);
}
} else {
result = super.convert(event);
}
return result;
}
@Data
private class RuleConfig {
private String reg;
private String replacement;
RuleConfig(String reg, String replacement) {
this.reg = reg;
this.replacement = replacement;
}
String apply(String message) {
return Pattern.compile(reg).matcher(message).replaceAll(replacement);
}
}
}
第三步:增加logback.xml文件妓布,整合規(guī)則與轉(zhuǎn)換邏輯
- 1姻蚓、引入我們第一步配置的脫敏規(guī)則屬性文件(這里要注意,屬性的scope匣沼,默認是local狰挡,詳細配置可以參考logback官網(wǎng))
- 2、配置我們第二步增加的轉(zhuǎn)換器释涛,同時配置conversionWord="xxx"加叁,當日志輸出的pattern中引用%xxx時,會通過此轉(zhuǎn)換順進日志輸出的轉(zhuǎn)換
配置如下
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<property scope="context" resource="./logback-desensitization-rule.properties"/>
<conversionRule conversionWord="msg"
converterClass="com.bk.framework.extension.logback.DesensitizationMessageConverter"/>
<conversionRule conversionWord="rid" converterClass="com.bk.framework.extension.logback.ClassicConverterExt"/>
<property name="CONSOLE_LOG_PATTERN"
value="%date{yyyy-MM-dd HH:mm:ss} | %boldYellow(%rid) | %highlight(%-5level) | %boldYellow(%thread) | %boldGreen(%logger) | %msg%n"/>
<appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>${CONSOLE_LOG_PATTERN}</pattern>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="stdout"/>
</root>
</configuration>
測試效果
- 編輯測試類
package com.bk.framework.extension;
import lombok.extern.slf4j.Slf4j;
/**
* @author BK
* @version V2.0
* @date 2019-06-03 23:43
*/
@Slf4j
public class ExtensionApplication {
public static void main(String[] args) {
log.info("mobile:{}", "13588889999");
log.info("userInfo:{}", "{\n" + " \"userName\":\"羅志祥\"唇撬,\n" + " \"idcard\":\"321183197701017846\",\n" + " \"password\":\"luozhixiang1234\",\n" + " \"mobile\":\"18888888888\"\n" + " }");
}
}
-
運行main方法
可以看到它匕,只需要增加一個轉(zhuǎn)換器,配置幾個正則窖认,就可以實現(xiàn)日志的脫敏豫柬,希望可以幫到大家告希,相關(guān)的代碼參考logback日志脫敏V1版本,傳送門項目地址.
關(guān)于優(yōu)化的一點兒思路
其實到現(xiàn)在烧给,我們可以實現(xiàn)日志脫敏燕偶,但是做法有些丑陋,不是很美觀础嫡,可以推薦一個思路
通過DTD或得schema文件 指么,定義自己的XML格式文件
- 設(shè)置對于json,xml榴鼎,等不同式日志的脫敏關(guān)鍵字的replacement等信息伯诬,主要目的是為了簡化配置,封裝正則的配置難度檬贰,后臺根據(jù)配置自動生成正則和替換規(guī)則
- 在logback start時姑廉,解析配置文件缺亮,讀取配置生成相應(yīng)的正則翁涤,進行脫敏
另一種優(yōu)化的思路
增加注解,對需要脫敏的字段進行標記關(guān)鍵字 @Desensitization(type=Desensitization.NAME) //或者 MOBILE 等其它萌踱,可以自己定義葵礼,只是需要在和配置文件中的統(tǒng)一
- 增加SpringBoot注解 EnableLogBackDesensitization(pageckage = "com.bk.test, com.bk.xxx")
- 參考Spring的component-scan,進行 包路徑掃描并鸵,把相關(guān)的字段鸳粉,根據(jù)分類收集起來,對正則中關(guān)鍵字部分替換
- 在收集完成之后打印日志時园担,進行脫敏
相關(guān)代碼會傳到我的github上届谈,相關(guān)的代碼參考logback日志脫敏V2版本,傳送門項目地址
一點兒建議
- 過大對象一般不建議打日志
對于一些過大的對象弯汰,比如查詢列表的結(jié)果建議不要打印艰山,如果需要打印的話,建議在DEBUG級別打印
一般這種日志咏闪,對于問題排查意義不大曙搬,而且這種日志導(dǎo)致日志文件巨增,同時如果日志被竊取會存在信息泄漏的風險 - 打印日志時一定要有危機意識
哪些日志可以打印鸽嫂,如些日志打印會有風險纵装,一定要做到心里有數(shù) - 打印error級別日志時,建議使用log.error("xxxxxx" ,e );
不要使用log.error("xxxxxx" ,e.getMessage() );
參考資料
https://logback.qos.ch/manual/configuration.html
寫在最后
這點兒東西寫了三遍据某,簡書是不是最近有問題橡娄,圖片展示不了,沒有發(fā)布的內(nèi)容總是丟失癣籽,這已經(jīng)是第三遍了挽唉。
歡迎大家討論扳还,本人才疏學(xué)淺,有不正確的地方還請斧正橱夭。 個人博客已經(jīng)開通氨距,感覺興趣可以收藏下:http://albk.tech , 內(nèi)容會同步更新簡書