Wednesday, September 13, 2017

Enabling HPKP (Http Public Key Pinning) in Tomcat

The Public Key Pinning Extension for HTTP (HPKP) is a security feature that tells a web client to associate a specific cryptographic public key with a certain web server to decrease the risk of MITM attacks with forged certificates.

Unfortunately Apache Tomcat does not support HPKP filter. So we have to write our own 'Global filter', Valve.
First you need to extract the public key information from your certificate or key file and encode them using Base64.
The following commands will help you extract the Base64 encoded information from a key file, a certificate signing request, or a certificate.
openssl rsa -in my-rsa-key-file.key -outform der -pubout | openssl dgst -sha256 -binary | openssl enc -base64
openssl ec -in my-ecc-key-file.key -outform der -pubout | openssl dgst -sha256 -binary | openssl enc -base64
openssl req -in my-signing-request.csr -pubkey -noout | openssl pkey -pubin -outform der | openssl dgst -sha256 -binary | openssl enc -base64
openssl x509 -in my-certificate.crt -pubkey -noout | openssl pkey -pubin -outform der | openssl dgst -sha256 -binary | openssl enc -base64
The following command will extract the Base64 encoded information for a website.
openssl s_client -servername www.example.com -connect www.example.com:443 | openssl x509 -pubkey -noout | openssl pkey -pubin -outform der | openssl dgst -sha256 -binary | openssl enc -base64

Example HPKP Header

Public-Key-Pins: 
  pin-sha256="cUPcTAZWKaASuYWhhneDttWpY3oBAkE3h2+soZS7sWs="; 
  pin-sha256="M8HztCzM3elUxkcjR2S5P4hhyBNf6lHkmjAHKhpGPWE="; 
  max-age=5184000; includeSubDomains; 
  report-uri="https://www.example.org/hpkp-report"
In this example, pin-sha256="cUPcTAZWKaASuYWhhneDttWpY3oBAkE3h2+soZS7sWs=" pins the server's public key used in production. The second pin declaration pin-sha256="M8HztCzM3elUxkcjR2S5P4hhyBNf6lHkmjAHKhpGPWE=" also pins the backup key. max-age=5184000 tells the client to store this information for two months, which is a reasonable time limit according to the IETF RFC. This key pinning is also valid for all subdomains, which is told by the includeSubDomains declaration. Finally, report-uri="https://www.example.net/hpkp-report" explains where to report pin validation failures.
Now create a Valve as described below.
  1. Create a Maven Java Application.
  2. Add the following dependency:
<!-- https://mvnrepository.com/artifact/org.apache.tomcat/catalina -->
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>catalina</artifactId>
<version>6.0.53</version>
<scope>provided</scope>
</dependency>
  1. Create your Java class and extend it from ValveBase.
  2. Implement the invoke(Request, Response) method.
  3. import org.apache.catalina.valves.ValveBase;
    
    public class GlobalFilterValve extends ValveBase {
    
        @Override
        public void invoke(Request request, Response response) throws IOException, ServletException {
     response.setHeader("Public-Key-Pins", "pin-sha256=\"cUPcTAZWKaASuYWhhneDttWpY3oBAkE3h2+soZS7sWs=\"; pin-sha256=\"M8HztCzM3elUxkcjR2S5P4hhyBNf6lHkmjAHKhpGPWE=\"; max-age=5184000; includeSubDomains");                getNext().invoke(request, response);
        }
    
    }
  4. Build your library (.jar) file
  5. Copy the jar file in the ${tomcat.home}/lib directory.
  6. Configure the server.xml to use your new valve. For example:
<valve className="org.devan.GlobalFilterValve"/>
  1. Start the server to see your new valve in action


Thank you !


Global custom filter in Apache Tomcat


We are aware about custom filters and built in filters available in Apache Tomcat.
If we are writing a custom java filter in our project, it will be look like follows,

@WebFilter("/*")
public class MyFilter implements Filter {

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, 
                         FilterChain chain) throws IOException, ServletException {

        HttpServletResponse httpResponse = (HttpServletResponse) response;
        //your custom filter or set custom response header here
        chain.doFilter(request, response);
    }
}
This custom filter can help us to track and filter all requests coming to our webappliation, but it can not act like a global filter for all webapplications hosted in Apache Tomcat.

Apache Tomcat supports custom Valve elements which can help us to make this happen.

Valve element represents a component that will be inserted into the request processing pipeline for the associated Catalina container (EngineHost, or Context)

Following are the steps to implement a custom Valve in Apache Tomcat:



  1. Create a Maven Java Application.
  2. Add the following dependency:
<!-- https://mvnrepository.com/artifact/org.apache.tomcat/catalina --><dependency> <groupId>org.apache.tomcat</groupId> <artifactId>catalina</artifactId> <version>6.0.53</version> <scope>provided</scope></dependency>
  1. Create your Java class and extend it from ValveBase.
  2. Implement the invoke(Request, Response) method.
  3. import org.apache.catalina.valves.ValveBase;
    
    public class GlobalFilterValve extends ValveBase {
    
        @Override
        public void invoke(Request request, Response response) throws IOException, ServletException {
            //Do your filter/setting header in response here
            getNext().invoke(request, response);
        }
    
    }
  4. Build your library (.jar) file
  5. Copy the jar file in the ${tomcat.home}/lib directory.
  6. Configure the server.xml to use your new valve. For example:
<valve className="org.devan.MyValve"/>
  1. Start the server to see your new valve in action


Thank you !!!