如何使用Log4J在日志文件中掩盖信用卡号码?

28
我们的Web应用程序需要符合PCI标准,即不能存储任何信用卡号码。该应用程序是主机系统的前端,该系统内部处理CC号码,并且正如我们刚刚发现的那样,有时仍会在其响应屏幕上显示完整的CC号码。默认情况下,这些响应的全部内容都以调试级别记录,并且从这些内容中解析出的内容可以在许多不同的位置记录。因此我无法追踪此类数据泄漏的来源。我必须确保我们日志文件中的CC号码被掩盖。
正则表达式部分不是问题,我将重复使用已在其他几个地方使用的正则表达式。然而,我找不到任何关于如何修改Log4J日志消息的好资源。过滤器似乎更加受限,只能决定是否记录特定事件,但无法更改消息的内容。我还发现了Log4J的ESAPI安全包装器API,乍一看似乎可以实现我的要求。但是,显然我需要用ESAPI logger类替换代码中的所有记录器 - 这很麻烦。我希望有一个更透明的解决方案。
您有任何想法如何从Log4J输出中屏蔽信用卡号码吗?
更新:基于@pgras的原始想法,这里是一个可行的解决方案:
public class CardNumberFilteringLayout extends PatternLayout {
    private static final String MASK = "$1++++++++++++";
    private static final Pattern PATTERN = Pattern.compile("([0-9]{4})([0-9]{9,15})");

    @Override
    public String format(LoggingEvent event) {
        if (event.getMessage() instanceof String) {
            String message = event.getRenderedMessage();
            Matcher matcher = PATTERN.matcher(message);

            if (matcher.find()) {
                String maskedMessage = matcher.replaceAll(MASK);
                @SuppressWarnings({ "ThrowableResultOfMethodCallIgnored" })
                Throwable throwable = event.getThrowableInformation() != null ? 
                        event.getThrowableInformation().getThrowable() : null;
                LoggingEvent maskedEvent = new LoggingEvent(event.fqnOfCategoryClass,
                        Logger.getLogger(event.getLoggerName()), event.timeStamp, 
                        event.getLevel(), maskedMessage, throwable);

                return super.format(maskedEvent);
            }
        }
        return super.format(event);
    }
}

注:

  • 我使用 + 而不是 * 进行掩码,因为我想区分这个记录器对CID进行了掩码的情况与后端服务器或其他人进行掩码的情况
  • 我使用简单的正则表达式,因为我不担心误报

该代码经过单元测试,所以我相信它能正常工作。当然,如果您发现可以改进它的地方,请告诉我 :-)


2
不确定为什么您不让超级解析器先将事件转换为字符串,然后再进行过滤。这样可以节省很多编码工作。 - Boris Hamanov
@avok00,因为LoggingEvent是不可变的。 - Péter Török
PCI DSS 3.0允许最多显示BIN和最后四位。当您有大量流量时,最好使用这些最大允许的参数进行掩码处理。这可以通过更改这两行来完成。 private static final String MASK = "$1++++++$3"; private static final Pattern PATTERN = Pattern.compile("([0-9]{6})([0-9]{6,10})([0-9]{4})"); - Neoecos
2个回答

16
你可以编写自己的布局并为所有记录器配置它...
布局有一个格式化方法,可以从包含日志消息的loggingEvent生成字符串...

你可以将新的布局制作成现有布局的组合:将不符合正则表达式的任何内容委托给它,然后删除或用星号标记包含信用卡号码的行。 - James B
谢谢,这似乎是可行的。虽然在这种情况下我更喜欢子类化而不是聚合。让我们稍微试验一下... - Péter Török

5

2
好的,更好是相对的 :-) 在这种情况下,我不想记录发卡人或校验和。 我实际上并不关心具体的卡号——我们不需要在日志或其他任何地方搜索它们。这就是为什么我也不关心误报的原因。唯一的问题是,绝不能让完整的卡号进入日志。但我同意,在其他情况下,亚当的解决方案可能更好。 - Péter Török

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接