0

Here is the custom valve class that I wrote.. All it does is to mask password before logging to access log files ...

package com.test;

import org.apache.catalina.valves.AccessLogValve;

public class FilteredAccessLogValve extends AccessLogValve {

    public void log(String message) {
        message = message.replaceAll("password=[^&]*", "password=***");
        super.log(message);
    }
}

I build this as a value-lib.jar and copied to $TOMCAT_HOME/lib folder.

Then I customized the loggin entry in server.xml as follows under entry localhost

  <Host name="localhost"  appBase="webapps"
        unpackWARs="true" autoDeploy="true">

    <!-- SingleSignOn valve, share authentication between web applications
         Documentation at: /docs/config/valve.html -->
    <!--
    <Valve className="org.apache.catalina.authenticator.SingleSignOn" />
    -->

    <!-- Access log processes all example.
         Documentation at: /docs/config/valve.html
         Note: The pattern used is equivalent to using pattern="common" -->
    <Valve className="com.test.FilteredAccessLogValve" directory="logs"
           prefix="localhost_access_log" suffix=".txt"
           pattern=" com.test.FilteredAccessLogValve %{X-Forwarded-For}i %h %l %S %u %t '%r' %s %b %D"/>        

           <!-- pattern="%h %l %u %t &quot;%r&quot; %s %b" /-->

  </Host>

Now, all my log requests are logged properly like this in localhost_access_log.2018-02-12.txt file

com.test.FilteredAccessLogValve - 0:0:0:0:0:0:0:1 - - - [12/Feb/2018:10:13:49 +0530] 'GET / HTTP/1.1' 200 11452 362
com.test.FilteredAccessLogValve - 0:0:0:0:0:0:0:1 - - - [12/Feb/2018:10:13:56 +0530] 'GET /manager/html HTTP/1.1' 401 2536 45
com.test.FilteredAccessLogValve - 0:0:0:0:0:0:0:1 - - - [12/Feb/2018:10:14:11 +0530] 'GET /host-manager/html HTTP/1.1' 401 2098 16
com.test.FilteredAccessLogValve - 0:0:0:0:0:0:0:1 - - - [12/Feb/2018:10:22:21 +0530] 'GET /host-manager/html HTTP/1.1' 401 2098 132
com.test.FilteredAccessLogValve - 0:0:0:0:0:0:0:1 - - - [12/Feb/2018:10:22:22 +0530] 'GET /favicon.ico HTTP/1.1' 200 21630 9
com.test.FilteredAccessLogValve - 0:0:0:0:0:0:0:1 - - - [12/Feb/2018:10:24:53 +0530] 'GET / HTTP/1.1' 200 11452 212
com.test.FilteredAccessLogValve - 0:0:0:0:0:0:0:1 - - - [12/Feb/2018:10:25:04 +0530] 'GET /?password=123 HTTP/1.1' 200 11452 10
com.test.FilteredAccessLogValve - 0:0:0:0:0:0:0:1 - - - [12/Feb/2018:10:46:53 +0530] 'GET /ROOT/ HTTP/1.1' 404 1075 52

The problem that i face is when I pass say a parameter like password=123 to the URL like this say http://localhost:8080?password=123.

I expect that to be logged as

com.test.FilteredAccessLogValve - 0:0:0:0:0:0:0:1 - - - [12/Feb/2018:11:01:02 +0530] 'GET /?password=*** HTTP/1.1' 200 11452 14

where as it is now logged as

com.test.FilteredAccessLogValve - 0:0:0:0:0:0:0:1 - - - [12/Feb/2018:11:01:02 +0530] 'GET /?password=123 HTTP/1.1' 200 11452 14

there is no masking happening for the password value.. I tried to put a debugger in eclipse for the jar through Remote Java application with Tomcat being started in "jpda start" mode.

There I could not see the control being transferred to this class at all..

So, I'm little confused on how it works because it takes the log formatting from the XML config that I added to server.xml but in the same XML config I mentioned the class name as "com.test.FilteredAccessLogValve" but that't not being picked

  • I removed the value-lib.jar from $TOMCAT_HOME/lib folder and started the tomcat. Now, tomcat start itself fails because it is unable to find the class "com.test.FilteredAccessLogValve" during startup. So, I'm sure that it loads the class from lib folder but I'm not sure why it is not being used when I ask it to do.. – MuthuChellappan Feb 12 '18 at 05:45

1 Answers1

0

At last I found the solution.. Problem was that I use tomcat 7 jar in development and testing the solution in tomcat 8.5 implementation..

Tomcat has changed the method signature for log() call in "AccessLogValve" class between these 2 versions. Earlier it was

public void log(String message);

now its been modified as

public void log(CharArrayWriter message);

and hence my custom implemetation has been changed to

package com.test;

import org.apache.catalina.valves.AccessLogValve;

public class FilteredAccessLogValve extends AccessLogValve {

    public void log(CharArrayWriter message) {
        String mymessage = message.toString();
        if(mymessage.toLowerCase().contains("password=")) {
            mymessage = mymessage.replaceAll("password=[^&]*", "password=***");

            CharArrayWriter maskedMessage = new CharArrayWriter(1024);
            try {
                maskedMessage.write(mymessage);
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            super.log(maskedMessage);
        } else {
            super.log(message);
        }
    }
}

As I overridden the proper method, now the logs for this http://localhost:8080/manager/html/list?password=123 calls are masked properly as shown below

com.test.FilteredAccessLogValve - 0:0:0:0:0:0:0:1 - C71654B9F585F83AAF6E9A4233BD7D8B tomcat [12/Feb/2018:13:01:06 +0530] 'GET /manager/html/list?password=***
com.test.FilteredAccessLogValve - 0:0:0:0:0:0:0:1 - C71654B9F585F83AAF6E9A4233BD7D8B tomcat [12/Feb/2018:13:01:11 +0530] 'GET /manager/html/list?password=***