Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to configure Tomcat AccessLogValve to hide passwords?

Is it possible to configure AccessLogValve to hide/replace passwords before they are written to the log file? I am not familiar with valves, perhaps there is a way to insert a custom filter/valve?

like image 770
Reuben Peter-Paul Avatar asked May 22 '13 18:05

Reuben Peter-Paul


1 Answers

Solution with custom AccessLogValve class

  1. Create a separate maven project:

    pom.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xmlns="http://maven.apache.org/POM/4.0.0"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
    
        <groupId>test</groupId>
        <artifactId>filtered-access-log-valve</artifactId>
        <version>1.0-SNAPSHOT</version>
    
        <dependencies>
            <dependency>
                <groupId>org.apache.tomcat</groupId>
                <artifactId>tomcat-catalina</artifactId>
                <version>7.0.70</version>
            </dependency>
        </dependencies>
    
    </project>
    

    FilteredAccessLogValve.java

    package test;
    
    import org.apache.catalina.valves.AccessLogValve;
    
    public class FilteredAccessLogValve extends AccessLogValve {
        @Override
        public void log(String message) {
            super.log(message.replaceAll("password=[^&]*", "password=***"));
        }
    }
    
  2. Build jar artifact from the project and copy it to tomcat/lib/

  3. In tomcat/conf/server.xml change access log valve class

    <Valve className="test.FilteredAccessLogValve" ...>

Another solution using servlet filter

Finally I came to this solution as it requires minimal tomcat configuration.

  1. In tomcat/conf/context.xml turn off the default access log valve by commenting the <Valve> tag.
  2. Create AdvancedAccessLogFilter class:

    package your.package;
    
    import javax.servlet.*;
    import javax.servlet.http.HttpServletRequest;
    import java.io.IOException;
    
    public class AdvancedAccessLogFilter implements Filter {
    
        private static final String PASSWORD_REGEX = "password=[^&]+";
        private static final String PASSWORD_MASK = "password=***";
        private FilterConfig filterConfig = null;
    
        public void destroy() {
            this.filterConfig = null;
        }
    
        public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {
            if (filterConfig == null) {
                return;
            }
    
            HttpServletRequest request = (HttpServletRequest) servletRequest;
    
            String maskedPath = request.getRequestURI()
                    + (request.getQueryString() == null ? "" : request.getQueryString().replaceAll(PASSWORD_REGEX, PASSWORD_MASK))
                    + " "
                    + request.getProtocol();
    
            request.setAttribute("maskedPath", maskedPath);
            chain.doFilter(request, servletResponse);
        }
    
        public void init(FilterConfig filterConfig) throws ServletException {
            this.filterConfig = filterConfig;
        }
    
        public void init(FilterConfig filterConfig) throws ServletException {
            this.filterConfig = filterConfig;
        }        
    }
    
  3. Register the servlet filter in your project's web.xml:

    <filter>
        <filter-name>AdvancedAccessLogFilter</filter-name>
        <filter-class>your.package.AdvancedAccessLogFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>AdvancedAccessLogFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    
  4. Create src/main/webapp/META-INF/context.xml with modified access log valve pattern:

    <?xml version='1.0' encoding='utf-8'?>    
    <Context>
        <WatchedResource>WEB-INF/web.xml</WatchedResource>
        <Valve
                className='org.apache.catalina.valves.AccessLogValve'
                directory='logs'
                prefix='localhost_access_log.'
                suffix='.txt'
                pattern='%h %t %m "%{maskedPath}r" %s %b %D'
        />
    </Context>
    

If you want to autowire some beans from the Spring context, add SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this); to the filter's init() method.

like image 77
Oleg Mikhailov Avatar answered Sep 22 '22 17:09

Oleg Mikhailov