As the internet is becoming increasingly accessible to people, the number of attacks trying to steal personal information is on the rise. We, as developers, need to protect our users' data, and sometimes, the protection needs to start even before they reach our web application.
This post is about how we can protect our users when they first request our website using HTTP Strict Transport Security (HSTS). To illustrate this, we'll build a sample app with Spring Boot and Java and walk through how to configure HSTS. We'll also be using the Spring Security project.
But first, let's learn a little more about what HSTS is and what kind of attack it prevents.
What Is HSTS, and What Does It Protect Us From?
HTTP Strict Transport Security (HSTS), as defined by the Internet Engineering Task Force (IETF)'s RFC6797, was designed to enforce that connections to a website may only occur within secure connections. This prevents browsers from just visiting the website using HTTP and then redirecting to HTTPS, as this may leave users vulnerable to man-in-the-middle attacks. But there's a question that you may be asking yourself.
What is a man-in-the-middle (MitM) attack?
Imagine you're in a coffee shop, and you connect to the Wi-Fi network called “BreWi-Fi." You log in to your bank account to make sure you have funds for extra chocolate drizzle. Unbeknownst to you, that network is from an attacker that is pretending to be the router inside the café. To you, the experience will be transparent, but they now can log all your activity. They can see in plain sight your user credentials, traffic, and other information you may not want to be stolen.
Knowing what we want to protect against, let's see how HSTS and Spring Security can help us.
How Does Spring Security and HSTS Protect Users?
Part of the Spring Project, Spring Security is the main component to handle security inside your application, including authentication and authorization. When you add Spring Security, it automatically adds a couple of security headers to the request. One of those headers is Strict-Transport-Security.
What this does is tell the browser that even if you don’t write the HTTPS protocol on the URL, the browser should enforce only using Transport Layer Security (TLS) for all its communication, instead of redirecting from the normal HTTP protocol. The reason for this is that, even if you're automatically redirected to https://, an attacker may have a small window to place any script before leaving the HTTP site.
As a caveat, the browser will still need to make the first request in HTTP, and then it will be added to the preload list. You can skip this step by going to https://hstspreload.org/ and submitting your domain to be included in Chrome's HSTS Preload List.
HSTS requires that your certificate comes directly from a certificate authority. You can use a service like Let’s Encrypt to generate free certificates for this tutorial.
To start learning and configuring HSTS, we first need a sample Spring Boot project. The benefit of using Spring Boot over regular Spring is that you can get a lot of default configurations out of the box. Head over to https://start.spring.io/ and select Java as your language. Feel free to customize as you desire. Then, on the right-hand side, click on the button where it says “Add Dependencies” and select the following:
Spring Web: This allows us to create our web application.
Thymeleaf: This will be our template engine.
Once you download the project, let’s first make sure the project runs. I built my demo using Gradle, but any commands needed will be provided for both Maven and Gradle.
Here is how the build.gradle file looks:
And here is the pom.xml file:
Building the App
In the root folder, run one of the following commands, depending on if you selected Maven or Gradle:
To run the application: mvn spring-boot:run (for Maven) or ./gradlew bootRun
To only build the jar: mvn clean package or ./gradlew bootJar
If you prefer to manually run the jar file, after calling the build command, you can run java -jar target/project-name.jar for Maven or java -jar build/lib/project-name.jar for Gradle.
To confirm that Spring Security is being detected, you can see in your terminal a line with the text “Using generated security password:”.
When you open your browser, you might see a login form with the text “Please sign in.” The default username is user, and the generated password is what appears in the terminal. Also, below that, you'll see the Tomcat port in which the application is running; the default port is :8080.
If you don’t see the login form, don’t panic. As long as you see the “Using generated security password” line in your console, security has been configured. If you still want to see this page, in the following section, I'll show you how to display it.
After logging in, the page will present you with a “Whitelabel Error Page." If you look closely, it will say that there was an unexpected error and give you a 404 status. This is because no controller or view has been routed to the default “/” domain.
Go to your src/main/resources/templates folder and create a file called home.html and write the following:
After reloading your project, open your browser. You should see the “Hello from Spring” message on the page. Right-click on the page and select "Inspect." Then, depending on your browser, make your way to the network tab and select the localhost from the list.
Now that we have a working project, let's first check where we can see all the headers Spring Security provides us.
Click on the Headers tab, and you'll see some of the default headers Spring Security adds. Not present is Strict-Transport-Security since it's hosted locally. You can visit https://spring.io/projects/spring-security and perform the same steps, and you will see the header. You can also try this after deploying your website and using a certificate.
Then create a new config package in your main project directory where your main Spring app is located, and inside create a file named SecurityConfiguration.
This class needs to extend the WebSecurityConfigurerAdapter. This class allows us to be able to extend and config the default security settings. You'll also need to add the @EnableWebSecurity and @Configuration annotations so that this class is picked up by Spring.
We need to override one of the configure methods, the one with the method signature of configure (HttpSecurity http). Here, we can chain options inside the HTTP parameter to customize the HTTP headers and requests.
If you were not met with the login page, here is where you would configure it to appear. As an example, here is how you would configure Spring to display the form:
Here we're telling the system to authorize the requests, that any request done to the website must be authenticated, and to display the login form.
To configure HSTS, you need to extend the http.headers().httpStrictTransportSecurity(). This provides three methods for you to customize your headers: includeSubdomains(), preload(), maxAgeInSeconds().
maxAgeInSeconds() accepts an int. This is where you determine how long HSTS should last in the browser's cache. If you want this preload to last six months, for example, you can set the value as 15,780,000. It's recommended that the value be greater than 10,368,000 seconds (120 days). Some examples of the age could be as follows:
0: This will instruct the browser to delete the HSTS policy
42 days: 3,628,800
6 months: 15,780,000
1 year: 31,536,000
2 years: 63,072,000
includeSubdomains() accepts a Boolean. It will tell the browser to include all subdomains (e.g., sampleA.example.com, sample.example.com).
When referencing RCF6797, they state that the developer should consider adding includeSubDomain as long as the business requirements permit it.
preload() takes a Boolean. Here, the directive tells the HSTS preload list that you want to be included. According to the hstspreload website, this should be opt-in and, unless sure, should not be included in the header. Since the list is dependent on the release schedule of the browser, by the time it goes into the list, there might be a need to remove a subdomain, which can take a large amount of time. The default value is false.
Now that we know how to enable HSTS with Spring, there may be some testing cases in which you may want to disable the header.
To disable HSTS, inside your configure method, add the following:
In some instances you might need to disable Spring Security without removing the package in its entirety.
Disable Spring Security
Removing the @EnableWebSecurity annotation will not disable security for the package; it will default to the SecurityAutoConfiguration class. If you want to disable it, you can add to your application.properties file the following:
Or you can also disable it by excluding the package in your main app:
Out of the box, Spring Security automatically protects against some of the more common security vulnerabilities. MitM attacks will become more common as people expect free Wi-Fi spots when they're out and about. The HTTP Strict Transport Security (HSTS) minimizes the risk of exposing user information, and it being added by default is one less thing you need to worry about as a developer.
This post was written by Kenneth Reyes. Kenneth is a Full stack developer that has worked in different industries and with clients of all levels. He specializes in Java/Spring, Vue, and DevOps. He strives to make programming interesting for people outside the field.