Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to require SSL in some environments using Spring Boot 2.0.0.M4+

While upgrading my Spring Boot applications through the 2.0.0 milestone releases, I've noticed that starting in 2.0.0.M4 security.require-ssl and other security configuration options are gone. I didn't find any mention of deprecations or a new approach in the current docs so I dug around and found the GitHub issue where the work originated. I applaud the goal in the GitHub issue to:

significantly simplify security configuration in 2.0.

and am happy change my patterns to upgrade, but I'm a little stuck on how to require SSL in specific environments. I know I can accomplish a similar outcome in my WebSecurityConfigurerAdapter configuration using http.requiresChannel().anyRequest().requiresSecure() but I don't want to enable this setting in every environment I run my applications in (e.g. pre-production environments without a certificate, local development on localhost). I know I could put some conditional logic in my WebSecurityConfigurerAdapter configuration but I like to keep my Spring configurations "environment agnostic" and keep my environment specific configurations in properties files specific to a profile.

After these recent simplifying changes to Spring Boot's security configuration, what's a recommended approach to require SSL in some environments?

like image 800
Leonhardt Koepsell Avatar asked Jan 30 '23 09:01

Leonhardt Koepsell


2 Answers

There are a few options depending on your setup.

Changing the RequestMatcher

If the only time you require HTTPS is in an environment with a custom header injected by a Proxy (i.e. X-Forwarded-* headers), you can do something like this:

http
    .requiresChannel()
        .requestMatchers( r -> r.getHeader("X-Forwarded-Proto") != null).requiresSecure()
        .and()
    // ...

The nice thing about this approach is that you do not need to do anything to turn HTTPS on or off. The downside is that there is a very minimal performance hit of comparing the headers. This performance hit should be negligible, but may not be aesthetically pleasing to some.

Conditional Configuration

Since this is Java Code you can always have a boolean flag that determines if you require HTTPS or not. For example, create a member variable and resolve it to Externalized Configuration with something like this:

@Value("${security.require_ssl}")
private boolean requireHttps;

Then use something like this:

if(this.requireHttps) {
    http
        .requiresChannel()
            .anyRequest().requiresSecure();
}

http
    // ...

This approach aligns nicely with Spring Boot 1.x approach and it does not suffer from the additional if statement in each request. However, it requires a property to be managed.

Profiles

As mentioned by Madhura you can also use @Profile. I find this approach is better suited if you have lots of differences between environments and not as ideal for something minor like determining if HTTPS is required. The problem with this approach when making a small change is you run into a lot of duplication.

like image 122
Rob Winch Avatar answered Feb 02 '23 11:02

Rob Winch


You're absolutely right about adding your own WebSecurityConfigurerAdapter for customizing security configuration. To keep that configuration environment agnostic, you can add an @Profile so that it can be switched on and off depending on the active profile. If it is only the ssl configuration that is environment agnostic, and the rest of the security configuration is the same for all environments, you can add a WebSecurityConfigurerAdapter that only configures http.requiresChannel().anyRequest().requiresSecure() that has the @Profile on it and order it such that it is the first one that kicks in.

like image 26
Madhura Bhave Avatar answered Feb 02 '23 10:02

Madhura Bhave