【问题标题】:Redact passwords in jetty request logs在码头请求日志中编辑密码
【发布时间】:2021-08-17 14:05:47
【问题描述】:

我正在开发一个通过 URL 查询字符串以纯文本形式发送一些敏感信息的应用程序。我的目标是自定义我的码头请求日志,以便在记录之前编辑或删除任何敏感信息。

到目前为止,我已经尝试制作一个自定义过滤器,它确实从查询字符串中删除了敏感信息,但是当我保持启用本机码头日志记录时,我得到了双重日志(一个有密码,一个没有密码),当我禁用本机时我没有收到无效流量(请求到达正确的服务但查询字符串有其他问题)。

我的问题是,有没有办法继续使用我的过滤器而不让码头记录具有相同信息的重复条目,或者是否有不同的方法来清理密码(如何实现不同的记录器,修改码头requestlog jar?)。我对开发世界几乎是全新的,所以我不知道有什么可能。

过滤以供参考

@Component
@Order(1)
public class RequestLogFilter implements Filter {

    private final static Logger log = LogManager.getLogger(RequestLogFilter.class);

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest) servletRequest;
        String uriPath = req.getRequestURI();
        String uri = req.getQueryString();

        uri = uri != null ? uri.replaceAll("password.+?&", "password=redacted&") : "";

        String method = req.getMethod();
        String ip = req.getRemoteAddr();
        String protocol = req.getProtocol();

        String logString = ip + " - - " + '"' + method + " " + uriPath + "?" + uri + " " + protocol + '"';
        log.info(logString);


        filterChain.doFilter(servletRequest, servletResponse);

    }

    @Override
    public void destroy() {
        log.warn("Destructing RequestLogFilter :{}");
    }
}

【问题讨论】:

    标签: logging jetty classpath


    【解决方案1】:

    注意

    将密码放在 URL 查询字符串中是个坏主意。

    当通过 HTTP 发送时(版本无关,HTTP/1.0、HTTP/1.1、HTTP/2 和 HTTP/3 在这里都具有相同的行为) 路径 + 查询(您的 URL/URI)在 HTTP 请求行中发送。

    这意味着https://example.com:8080/api/user?id=bob&password=axolotl 的 URL/URI 将发送 类似这样的东西(HTTP/1.1 示例)

    GET /api/user?id=bob&password=axolotl HTTP/1.1
    Host: example.com:8080
    Connection: close
    

    该请求行在许多地方都按原样记录,但标头没有。 该请求行还可以在解析步骤期间显示为日志记录,或用于报告错误。

    整个请求行也作为RequestDispatcher 的一部分出现在 Servlet 规范中 基于HttpServletRequest.getAttributes() 中的调用,也可以记录。

    浏览器也使用该查询字符串将Referer 标头发送到其他资源。

    该查询字符串现在是您的 HTTP 客户端(浏览器和各种 http 库, 包括其余库,甚至电话软件)将缓存该请求和查询字符串。 浏览器历史记录也会显示该查询字符串。

    查询字符串也受 Servlet Spec 中的额外行为影响。

    如果你使用HttpServletRequest.getParameter("password"),这会强制整个请求 读取以在查询字符串和请求正文内容中查找“参数”(如果 请求正文内容恰好是 application/x-www-form-urlencodedmultipart/form-data 然后 该参数也可以从请求正文内容中获取它的值。

    如果您(或任何第 3 方库)使用 Servlet 规范包含/排除功能,那么 查询字符串必须从原始请求合并到子请求以满足 包含/排除行为。

    关于不使用密码查询部分的其他一些建议。

    最简单、最安全的解决方案

    如果您可以从GET 和查询字符串切换到POST 并使用 application/x-www-form-urlencodedmultipart/form-data 请求正文内容 你会看到很多好处,包括码头各处都没有伐木 用于请求正文内容。

    记录特定的解决方案(一个可怕的黑客)

    如果您仍想在日志记录端处理此问题,您基本上必须执行以下操作。

    从所有日志库(java.util.logging、log4j、slf4j、jakarta-commons-logging、juli logging 等)捕获所有日志输出事件 将它们路由到单个日志记录实现(您可以使用 slf4j 轻松完成此操作,只需选择正确的 slf4j jar 组合) 然后有一个自定义日志实现,查看您需要编辑的关键字的所有日志消息 并在将日志消息附加到输出之前相应地更改日志消息(无论是控制台还是日志文件等)

    log4j 从 1.2.15+ 开始支持修改日志消息

    见:LOG4J: Modify logged message using custom appender

    【讨论】:

    • 谢谢你,我也计划最终从查询字符串中获取密码,但这会有点。我对日志记录不太熟悉,所以我只是想确认一些事情。如果我在获取战争文件的独立码头服务器中运行我的应用程序,自定义附加程序将停止码头的本机日志,还是需要手动关闭它们? (我今天会试试这个,所以到那时我可能会弄清楚)。
    • 如果类加载器中有slf4j-api-<ver>.jar,Jetty 9 将使用它。 (Jetty 10+ 只会使用 slf4j-api)你也可以配置 Jetty 9 使用 java.util.logging。在 Jetty 9 中,requestlog 是高度自定义的。在 Jetty 10 中,RequestLog 超级灵活,可以使用 slf4j-api 进行输出路由/过滤。
    • 如果您有未编辑的日志想要发送给第三方,您可以使用 Sublime Text 追溯编辑它们 - 请参阅 codingrob.medium.com/…
    猜你喜欢
    • 2020-02-01
    • 2016-08-19
    • 1970-01-01
    • 1970-01-01
    • 2018-07-04
    • 2014-02-11
    • 2019-01-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多