I just released the Starter Class of "Learn Spring Security":
1. Overview
This tutorial shows how to use HTTPS to protect your application’s login page using Spring’s Channel Security feature.
Using HTTPS for authentication is crucial to protect the integrity of sensitive data when in transport.
The article builds on top of the Spring Security Login tutorial by adding an additional layer of security. We highlight the steps needed to secure the authentication data by serving the login page through the encoded HTTPS channel.
2. Initial Setup Without Channel Security
Let’s start out with the security configuration explained in the aforementioned article.
The web-app allows users to access:
- /anonymous.html without authentication,
- /login.html, and
- other pages (/homepage.html) after a successful login.
The access is controlled by the following configuration:
@Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .antMatchers("/anonymous*") .anonymous(); http.authorizeRequests() .antMatchers("/login*") .permitAll(); http.authorizeRequests() .anyRequest() .authenticated();
Or via XML:
<http use-expressions="true"> <intercept-url pattern="/anonymous*" access="isAnonymous()"/> <intercept-url pattern="/login*" access="permitAll"/> <intercept-url pattern="/**" access="isAuthenticated()"/> </http>
At this point, the login page is available at:
http://localhost:8080/spring-security-login/login.html
Users are able to authenticate themselves through HTTP, however this is insecure as passwords will be sent in plain text.
3. HTTPS Server Configuration
To only deliver the login page over HTTPS – your web-server must be able to serve HTTPS pages. This requires that SSL/TLS support is enabled.
Note that you can either use a valid certificate or, for testing purposes, you can generate your own.
Let’s say we’re using Tomcat and rolling our own certificate. We’ll first need to create a keystore with a self-signed certificate.
Generating the keystore can be done issuing the following command in the terminal:
keytool -genkey -alias tomcat -keyalg RSA -storepass changeit -keypass changeit -dname 'CN=tomcat'
This will create a private a key and a self-signed certificate in the default keystore for your user profile, in your home folder.
The next step is to edit conf/server.xml to make it look like this:
<Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" /> <Connector port="8443" protocol="org.apache.coyote.http11.Http11NioProtocol" maxThreads="150" SSLEnabled="true" scheme="https" secure="true" clientAuth="false" sslProtocol="TLS" keystoreFile="${user.home}/.keystore" keystorePass="changeit" />
The second SSL/TLS <Connector> tag is usually commented out in the config file so uncommenting and adding keystore information is all that is needed. Further information is available in Tomcat’s related documentation.
With the HTTPS configuration in place, the login page can now be served under the following URL as well:
https://localhost:8443/spring-security-login/login.html
Web-servers other than Tomcat would require different but likely similar configuration.
4. Configuring Channel Security
At this point, we are able to serve the login page both under HTTP and HTTPS. This section explains how to mandate the usage of HTTPS.
To require HTTPS for the login page modify your security configuration by adding the following:
http.requiresChannel() .antMatchers("/login*").requiresSecure();
Or add the requires-channel=”https” attribute to your XML config:
<intercept-url pattern="/login*" access="permitAll" requires-channel="https"/>
After this point users could login only via HTTPS. All relative links e.g. a forward to /homepage.html will inherit the protocol of the original request and will be served under HTTPS.
When mixing HTTP and HTTPS request inside a single web app, there are additional aspects to be aware of and that require further configuration.
5. Mixing HTTP and HTTPS
From the security perspective, serving all requests under HTTPS is considered a best practice.
When using HTTPS exclusively is not an option, we can configure Spring to use HTTP by appending the following to the config:
http.requiresChannel() .anyRequest().requiresInsecure();
Or add requires-channel=”http” attributes to the XML:
<intercept‐url pattern="/**" access="isAuthenticated()" requires‐channel="http"/>
This instructs Spring to use HTTP for all requests that are not explicitely configured to use HTTPS, but at the same time it breaks the original login mechanism. The following sections explain the underlying cause.
5.1. A Custom Login Processing URL over HTTPS
The security configuration in the original security tutorial contains the following:
<form-login login-processing-url="/perform_login"/>
Without forcing /perform_login to use HTTPS a redirect would happen to the HTTP variant of it, losing the login information sent with the original request.
To overcome this we need to configure Spring to use HTTPS for the processing URL:
http.requiresChannel() .antMatchers("/login*", "/perform_login");
Notice the extra argument /perform_login passed to the antMatchers method.
The equivalent in the XML configuration requires adding a new <intercept-url> element to the config:
<intercept-url pattern="/perform_login" requires-channel="https"/>
If your own application is using the default login-processing-url (which is /login) you don’t need to configure this explicitly as the /login* pattern already covers that.
With the configuration in place, users are able to login, but are not able access authenticated pages e.g. /homepage.html under the HTTP protocol, because of Spring’s session fixation protection feature.
5.2. Disabling session-fixation-protection
Session fixation is a problem which can’t be avoided when switching between HTTP and HTTPS.
By default Spring creates a new session-id after a successful login. When a user loads the HTTPS login page the user’s session-id cookie will be marked as secure. After logging in, the context will switch to HTTP and the cookie will be lost as HTTP is insecure.
To avoid this setting session-fixation-protection to none is required.
http.sessionManagement() .sessionFixation() .none();
Or via XML:
<session-management session-fixation-protection="none"/>
Disabling session fixation protection might have security implications, therefore you need to weigh the pros and cons if you’re concerned about session fixation based attacks.
6. Test
After applying all these configuration changes accessing /anonymous.html without logging in (using either http:// or https://) will forward you to the page through HTTP.
Opening other pages directly like /homepage.html should get you forwarded to the login page via HTTPS and after login you will be forwarded back to /homepage.html using HTTP.
7. Conclusion
In this tutorial we’ve taken a look on how to configure a Spring web-application which communicates through HTTP except for the login mechanism. However new modern web-applications should almost always use HTTPS exclusively as their communication protocol. Lowering security levels or turning off security features (like session-fixation-protection) is never a good idea.
This tutorial and the one it is based on shares the same codebase which is available at GitHub. The channel security configuration can be enabled by listing https as an active Spring profile.