◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。
在当今数据驱动的世界中,数据安全最为重要。日志框架在应用程序监控和调试中发挥着至关重要的作用,但它们可能会无意中暴露不应该暴露的敏感信息。日志屏蔽是一种有效混淆日志消息中敏感数据、保护机密信息的技术。
logback 是 java 应用程序中功能强大且最常用的日志框架。它提供灵活的配置选项,包括将日志事件格式化为 json 对象的能力。它是 log4j 框架的继承者,由于其功能和易用性而迅速流行起来。它由 logger、encoders、layout、appender、encoder 组成。
logger: logger 是日志消息的上下文。应用程序将与此类交互以创建日志消息。
编码器: 编码器是在 logback 0.9.91 中引入的,负责将事件转换为字节数组以及将该字节数组写入 outputstream。作为布局引入的编码器只能将事件转换为字符串,这将其范围限制为非二进制输出。
布局: 布局负责根据用户的意愿格式化日志请求,而附加程序负责将格式化的输出发送到其目的地。
立即学习“Java免费学习笔记(深入)”;
appenders: 在 logback 中,输出目的地称为 appender。这会将日志消息放置在其最终目的地中。一个 logger 可以有多个 appender。目前,控制台、文件、远程套接字服务器、mysql、postgresql、oracle 和其他数据库、jms 和远程 unix syslog 守护进程都存在附加程序。
logstash-logback-encoder 库是增强 spring boot 应用程序日志记录功能的宝贵工具。它提供了一种以结构化 json 格式格式化日志消息的便捷方法,使日志聚合和分析工具(例如 logstash)可以轻松使用它们。 json 格式提供了一种结构化且机器可读的方式来记录信息,使其成为高级日志分析和安全措施的理想选择。 logstash 的好处
json 自定义 logstash 允许您自定义 json 输出以包含特定字段和元数据。
动态字段它还允许根据应用程序上下文动态添加字段来记录事件。
提高了可读性 json 格式为日志事件提供了清晰且易于阅读的结构。
增强的搜索和分析日志聚合工具可以轻松解析和查询 json 日志。
机器解析 json 日志非常适合自动分析和警报系统。
这里的主要目标是提供一种解决方案来屏蔽可在运行时定制和配置的数据。
这是我们的简单要求
第 1 步
创建 spring boot 应用程序。该解决方案将适用于任何基于 java 的应用程序,只需很少的定制。
第 2 步
配置所有正则表达式以屏蔽数据。请记住,正则表达式在资源利用率方面的成本很高。确保您正在调整正则表达式。正则表达式组将允许我们从字符串中选择所需的子字符串。
第三步
创建一个类并实现 messagejsonprovider。该接口来自logstash,允许我们在打印到附加程序之前自定义消息。每个日志消息都会调用此接口中的 writeto 方法。
在start()方法中读取所有正则表达式并准备包含所有maskingrule的logmasker。该方法来自 abstractjsonprovider,只是将进程启动标记为 true。
maskingrule 将保存正则表达式模式和一个函数。此函数替换日志中识别的字符串。
@data public class maskingmessagingprovider extends messagejsonprovider { public static final string default_rules_delimiter = ","; private logmasker logmasker; private string rules; public maskingmessagingprovider() { super(); } @override public void start() { super.start(); this.logmasker = logmasker.create(stringutils.tokenizetostringarray(rules, default_rules_delimiter)); } @override public void writeto(jsongenerator generator, iloggingevent event) throws ioexception { if (isstarted()) { jsonwritingutils.writestringfield(generator, getfieldname(), logmasker.mask(event.getformattedmessage())); } } } class logmasker { private maskingrule[] masks; public logmasker(maskingrule[] masks) { super(); this.masks = masks.clone(); } public static logmasker create(string[] rules) { return new logmasker(arrays.stream(rules).map(rule -> maskingrule.create(rule)).toarray(maskingrule[]::new)); } public string mask(string input) { string transformed = input; for (maskingrule m : masks) { transformed = m.mask(transformed); } return transformed; } } class maskingrule { public static final int reg_ex_default_group_selector = 2; public static final string default_replacement = "*"; private pattern pattern; private unaryoperator<string> replacement; public maskingrule(pattern maskpattern, unaryoperator<string> replacement) { super(); this.pattern = maskpattern; this.replacement = replacement; } public static maskingrule create(string rule) { return new maskingrule(pattern.compile(rule), (in) -> maskingrule.maskdatawithreplacement(in, default_replacement)); } public string mask(string transformed) { matcher matcher = pattern.matcher(transformed); stringbuffer sb = new stringbuffer(); while (matcher.find()) { matcher.appendreplacement(sb, replacement.apply(getdatatobemasked(matcher))); } matcher.appendtail(sb); return sb.tostring(); } private static string maskdatawithreplacement(string input, string replacement) { int repetition = !stringutils.haslength(input) ? 0 : input.length(); return string.join("", collections.ncopies(repetition, replacement)); } private static string getdatatobemasked(matcher matcher) { if (matcher.groupcount() > 1) { return matcher.group(reg_ex_default_group_selector); } return matcher.groupcount() > 0 ? matcher.group(1) : ""; } }
步骤 4
在 logback-spring.xml 文件中配置类。
<configuration> <springproperty scope="context" name="rules" source="app.logging.masking.rules" defaultvalue=""/> <appender name="console" class="ch.qos.logback.core.consoleappender"> <encoder class="net.logstash.logback.encoder.loggingeventcompositejsonencoder"> <providers> <provider class="com.daya.logging.logstash.maskingmessagingprovider"> <rules>${rules}</rules> <rulesdelimiter>${rulesdelimiter}</rulesdelimiter> <ruledelimiter>${ruledelimiter}</ruledelimiter> </provider> <threadname/> <timestamp/> <loglevel/> <loggername/> <mdc/> <version/> <stacktrace/> </providers> </encoder> </appender> <root level="info"> <appender-ref ref="console"/> </root> </configuration>
步骤 5
运行应用程序。为简单起见,我采用了一个保存数据的字符串并在应用程序启动时打印它。
@SpringBootApplication @Slf4j public class LogDataMaskingApplication { public static void main(String[] args) { SpringApplication.run(LogDataMaskingApplication.class, args); LogDataMaskingApplication.maskingTest(); } public static void maskingTest() { String data = "{"loginName":"maskingtest","phoneNumber":"9898981212","password":"Masking@123"}"; log.info(data); } }
这是非常基本的解决方案,并且根据消息摘要等要求有很大的增强空间...
您可以在 github 上找到代码。
如有任何问题请留言。
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。