How to Setup a HTTP Strict Transport Security to a Website or Java Application with Custom HSTS Filter

1. Overview:

In this post, We'll learn how to enable/add HTTP Strict Transport Security (HSTS) Header to Tomcat 8 using a built-in filter. And also discuss how to add custom HSTS filter in a java web application.

HSTS | How To Setup Strict Transport Security (HSTS)

Learn Enabling/Adding HTTP Strict Transport Security (HSTS) Header to a Website in Tomcat or Any Server


As well as a solution to add HSTS to any web-site using web.config.

At last, will talk about the testing methodology to make sure HSTS is enabled for a website.

About HSTS:


HTTP Strict Transport Security (HSTS) instructs web browsers to only use secure connections for all future requests when communicating with a web site. Doing so helps prevent SSL protocol attacks, SSL stripping, cookie hijacking, and other attempts to circumvent SSL protection.

Enabling HSTS Header



Enabling HTTP Strict Transport Security (HSTS) for Tomcat 8:

HSTS is abbreviated as HTTP Strict Transport Security. HTTP Strict Transport Security (HTTP ) is a web security policy mechanism that helps to protect websites against protocol downgrade attacks and cookie hijacking.

Most of the companies do the Security vulnerability scan for your application and maybe saying missing HTTP Strict Transport Security is missing as part of the response. Please add an HTTP header to the response.

Adding HTTP Strict Transport Security(HSTS) in java, Tomcat

This can be done in two ways.

1) Tomcat 8 built-in filter
2) Changes to web.config
3) Implementing Custom Filter
4) How to test HSTS is enabled for a website.

2.  Tomcat 8 built-in filter for HSTS

Before doing this, You must enable HTTPS redirect protocol in the server. This post designed for the tomcat server.
When we receive the HTTP request then internally need to redirect to HTTPS. below is the configuration for this.



2.1 tomcat8/conf/server.xml


<Connector executor="tomcatThreadPool"
port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />


2.2 Tomcat server configuration:


If your application is deployed in tomcat 8 server, then there is a built-in filter that will fulfill all your needs. HttpHeaderSecurityFilter class is provided by the Tomcat server. We must have tomcat version 8 to enable this feature.

Built in filter: org.apache.catalina.filters.HttpHeaderSecurityFilter

HSTS_HEADER_NAME = "Strict-Transport-Security"; is a predefined value and can not be changed by the developer. This constant is part of the HttpHeaderSecurityFilter.



Below are the parameters which can be configured in web.xml file.

  private boolean hstsEnabled;
private int hstsMaxAgeSeconds;
private boolean hstsIncludeSubDomains;
private boolean hstsPreload;
private String hstsHeaderValue;

Add the following configuration in tomcat8/conf/web.xml file which is loaded during server startup.


 <filter>
<filter-name>httpHeaderSecurity</filter-name>
<filter-class>org.apache.catalina.filters.HttpHeaderSecurityFilter</filter-class>
<async-supported>true</async-supported>
<init-param>
<param-name>hstsMaxAgeSeconds</param-name>
<param-value>31536000</param-value>
</init-param>
<init-param>
<param-name>hstsIncludeSubDomains</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>hstsPreload</param-name>
<param-value>true</param-value>
</init-param>
</filter>


<filter-mapping>
<filter-name>httpHeaderSecurity</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
</filter-mapping>

After adding, we should be now able to see in the response header.



Strict-Transport-Security: max-age=31536000; includeSubDomains

The main advantage of configuring at the server level is this is applicable for all the application that deployed in this server and no need to configure for each application.

The drawback is we must take note that while upgrading the tomcat server, we must do this change otherwise way be open for attackers.

3. Adding HSTS Header to Websites in web.config


Some websites and blogs say that to implement HSTS in IIS7+ you should just add the CustomHeader require for HSTS like this in your web.config. This is NOT correct:



<system.webServer>
<httpProtocol>
<customHeaders>
<add name="Strict-Transport-Security" value="max-age=31536000"/>
</customHeaders>
</httpProtocol>
</system.webServer>

This isn't technically to spec. The problem here is that you're sending the header ALWAYS even when you're not under HTTPS.

Note the first rule directs to a secure location from an insecure one. The second one adds the HTTP header for Strict-Transport-Security. The only thing I might change would be to formally canonicalize the www. prefix versus a naked domain.

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<rewrite>
<rules>
<rule name="HTTP to HTTPS redirect" stopProcessing="true">
<match url="(.*)" />
<conditions>
<add input="{HTTPS}" pattern="off" ignoreCase="true" />
</conditions>
<action type="Redirect" url="https://{HTTP_HOST}/{R:1}"
redirectType="Permanent" />
</rule>
</rules>
<outboundRules>
<rule name="Add Strict-Transport-Security when HTTPS" enabled="true">
<match serverVariable="RESPONSE_Strict_Transport_Security"
pattern=".*" />
<conditions>
<add input="{HTTPS}" pattern="on" ignoreCase="true" />
</conditions>
<action type="Rewrite" value="max-age=31536000" />
</rule>
</outboundRules>
</rewrite>
</system.webServer>
</configuration>


4. Implementing HSTS Custom Filter


We can write our own filter to add HSTS in response to every request. In some cases, 1st methodology will not work even if we are using tomcat 8+ version.

Our custom filter class must implements interface javax.servlet.Filter and provide implementations for init(), doFilter() , destroy() methods.

I have written a custom filter named HSTSFilter class and should be configured in the web.xml file. See the below two steps for custom HSTS filter implementation.

4.1 Custom HSTS Filter Class



package com.java.w3shools;

import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class HSTSFilter implements Filter {
private static final String HEADER_NAME = "Strict-Transport-Security";
private static final String MAX_AGE_DIRECTIVE = "max-age=%s";
private static final String INCLUDE_SUB_DOMAINS_DIRECTIVE = "includeSubDomains";
private static final Logger logger = LoggerFactory.getLogger(HSTSFilter.class);

private int maxAgeSeconds = 0;
private boolean includeSubDomains = false;
private String directives;

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
logger.debug("request.isSecure() :: " + request.isSecure());
System.out.println("request.isSecure() :: " + request.isSecure());

if (request.isSecure() && response instanceof HttpServletResponse) {
HttpServletResponse res = (HttpServletResponse) response;
res.addHeader(HEADER_NAME, this.directives);
}
chain.doFilter(request, response);
}

public void init(FilterConfig filterConfig) throws ServletException {
maxAgeSeconds = Integer.parseInt(filterConfig.getInitParameter("maxAgeSeconds"));
includeSubDomains = "true".equals(filterConfig.getInitParameter("includeSubDomains"));

if (this.maxAgeSeconds <= 0) {
throw new ServletException("Invalid maxAgeSeconds value :: " + maxAgeSeconds);
}

this.directives = String.format(MAX_AGE_DIRECTIVE, this.maxAgeSeconds);
if (this.includeSubDomains) {
this.directives += (" ; " + INCLUDE_SUB_DOMAINS_DIRECTIVE);
}
System.out.println("directives :: "+directives);
}

@Override
public void destroy() {
}
}

4.2 Web.xml:

After creating our HSTSFilter then it must be configured in web.xml as below.

 <filter>
<filter-name>HSTSFilter</filter-name>
<filter-class>com.java.w3shools.HSTSFilter</filter-class>
<init-param>
<param-name>maxAgeSeconds</param-name>
<param-value>31536000</param-value>
</init-param>

<init-param>
<param-name>includeSubDomains</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>HSTSFilter</filter-name>
<url-pattern>*</url-pattern>
</filter-mapping>

5. HSTS Testing

How to test HSTS is enabled or not?


Implementing HSTS is one challenging task and the next testing is another challenging part.

How to test HSTS changes that are applied to the web application?

There are mainly three ways.

5.1 Using the UNIX curl command

curl -s -D-  www.domain-name.com | grep Strict

Result expected:

Eg. curl -s -D-  http://localhost:8080/HSTSFilterDemo/index.html | grep Strict

HSTS-Test-curl-command


5.2 Chrome extension Postman 

Postman is a free add on in chrome browser. The result would be as below.


HSTS-Test-chrome-postman



5.3 Using third party website

There are many websites that check for HSTS is enabled for a website. Here are some examples of websites.

https://tools.geekflare.com/tools/hsts-test
https://hstspreload.org/


6. Conclusion


In this tutorial, We have seen what is HSTS and how to implement using a tomcat built-in filter and custom HSTS filter.

Next, Seen a solution to add HSTS to any web-site using web.config in IIS7 servers.

In the further article, we discussed testing whether strict-transport-security is added as part of a response or not.

The complete project source code showed in this article is available over GitHub.

0 Comments