Require HTTPS with Spring Security behind a reverse proxy - java

I have a Spring MVC application secured with Spring Security. The majority of the application uses simple HTTP to save resources, but a small part processes more confidential information and requires an HTTPS channel.
Extract from the security-config.xml :
<sec:http authentication-manager-ref="authenticationManager" ... >
...
<sec:intercept-url pattern="/sec/**" requires-channel="https"/>
<sec:intercept-url pattern="/**" requires-channel="http"/>
</sec:http>
All worked fine until we decided to migrate it to the main server, where the application servers run behind reverse proxies. And as now HTTPS is processed by the reverse proxies the application server only sees HTTP requests, and disallows access to the /sec/** hierarchy.
After some research, I found that the proxies add a X-Forwarded-Proto: https header (*), but in Spring Security HttpServletRequest.isSecure() is used to determine the channel security offered (extract from SecureChannelProcessor javadoc).
How can I tell Spring Security that a X-Forwarded-Proto: https header is enough for a secure request?
I know I could report that part on proxies configuration, but the proxies administrator really does not like that solution, because there are many application behind the proxies and the configuration could grow to a non manageable state.
I an currently using Spring Security 3.2 with XML config, but I'm ready to accept answers based on Java config and/or more recent version.
(*) Of course, the proxies remove the header if it was present in incoming request, so the application can be confident in it.

Kind of a followup to NeilMcGuigan's answer that showed that the solution was servlet container side.
Tomcat is even better. There is a valve dedicated to masking the side effects of a reverse proxy. Extract from Tomcat documentation for Remote IP Valve:
Another feature of this valve is to replace the apparent scheme (http/https), server port and request.secure with the scheme presented by a proxy or a load balancer via a request header (e.g. "X-Forwarded-Proto").
Example of the valve configuration :
<Valve className="org.apache.catalina.valves.RemoteIpValve"
internalProxies="192\.168\.0\.10|192\.168\.0\.11"
remoteIpHeader="x-forwarded-for" proxiesHeader="x-forwarded-by"
protocolHeader="x-forwarded-proto" />
That way with no other configuration of the application itself, the call to Request.isSecure() will return true if the request contains a header field of X-Forwarded-Proto=https.
I had thought of two other possibilities, but definitively prefere that one :
use a filter active before Spring Security ChannelProcessingFilter to wrap the request with a HttpServletRequestWrapper overriding isSecure() to process a X-Forwarded-Proto header - need writing and testing the filter and the wrapper
use a Spring BeanPostProcessor to look for a ChannelProcessingFilter and manually inject a ChannelDecisionManager able to consider the X-Forwarded-Proto header - really too low level

Spring Boot makes it dead simple (at least with embedded Tomcat).
1. Add the following lines to your application.properties:
server.forward-headers-strategy=native
server.tomcat.remote-ip-header=x-forwarded-for
server.tomcat.protocol-header=x-forwarded-proto
2. Do the following trick with your HttpSecurity configuration.
// final HttpSecurity http = ...
// Probably it will be in your `WebSecurityConfigurerAdapter.configure()`
http.requiresChannel()
.anyRequest().requiresSecure()
Source is Spring Boot reference guide
84.3 Enable HTTPS When Running behind a Proxy Server
Please also check the answer below for a specifics related to Spring Boot 2.2

If your site is HTTPS and you're running Apache Tomcat behind another system that's handling TLS termination, you can tell Tomcat to "pretend" that it's handling the TLS termination.
This makes request.isSecure() return true;
To do so, you need to add secure="true" to your Connector config in server.xml.
https://tomcat.apache.org/tomcat-7.0-doc/config/http.html
See also the scheme attribute.

Related

Spring MVC - How to support both http and https at the same time in Spring MVC web project

How can I support both HTTP and https at the same time in Spring MVC web project deployed on tomcat7 (and 8). I have configured every thing at Tomcat level.
HTTPS work if I use the following code by extending WebSecurityConfigurerAdapter
.and().requiresChannel().anyRequest().requiresSecure()
but then it does not allow HTTP, I need to support both HTTP AND HTTPS at the same time for same endpoint
example:
http://example.com/hello-world
https://example.com/hello-world
Changing your https configuration from .anyRequest() to a specific secure url eg. http.requiresChannel().antMatchers("/secure*").requiresSecure(); and making the rest Insecure as follows http.requiresChannel().anyRequest().requiresInsecure(); will solve the issue.
This instructs Spring to use HTTP for all requests that are not explicitly configured to use HTTPS.

How to turn off authentication for websockets in Spring Security?

I've got a Java EE application which uses Spring Security for authentication (configured with Java config). The same application contains a websocket server implemented with javax.websocket package.
My question is how to turn off Spring Security authentication for any incoming websocket connections without turning off HTTP authentication?
You can have all requests matching a particular pattern bypass the security filter. For example, if your WS endpoint is
/websocket you can add this to your configuration:
<http pattern="/websocket/**" security="none" />
I don't like to configure Spring programmatically so I don't know how this is done with Java code, but after a 10,000 feet look at the
classes I think it's maybe done with a WebSecurity + WebSecurityConfigurerAdapter setup.

Spring Boot Actuator adding X-Frame-Options = DENY to all endpoints (particularly error endpoint)

I am attempting to set the X-Frame-Options to DENY for all management endpoints, particularly the /error endpoint. I have the following in my application.properties of my Spring Boot application.
security.headers.frame=true
management.security.enabled=true
management.port=8001
When I go to http://localhost:8001/error I do not have the X-Frame-Options header, however the http://localhost:8001/trace endpoint does have the header. How do I configure my application.properties or what do I need to override to get that response header for the error endpoint?
Going through the current Spring Boot source (1.1.7.RELEASE), I don't see anyway that you can do what you want without totally doing away with the Security auto-configuration.
That is because in order for an endpoint to be eligible for the customized HTTP Headers (like X-Frame-Options) it needs to be a bean in the parent context (the one that is associated with the application on the normal port) that implements MvcEndpoint. Such beans are HealthMvcEndpoint, JolokiaMvcEndpoint etc.
My statement adove can be viewed in code at ManagementSecurityAutoConfiguration in the ManagementWebSecurityConfigurerAdapter.configure method (endpointHandlerMapping is created from the MvcEndpoint implementation beans).
The error page for the management app, is ManagementErrorEndpoint that is created in errorEndpoint of EndpointWebMvcChildContextConfiguration which is triggered when the child context is created (due the inclusion of the management app), which is too late to be included in the endpoints that supported for HTTP Headers customization
The /error endpoint is not an Actuator Endpoint and it's not secured by default (lots of errors can happen when a user is not authenticated). You could maybe make it available to anonymous users, but my guess is not even that would prevent some nasty infinite loops, where there is a mistake in the security configuration. I'm sure there's another way to add the header without Spring Security?

Spring Security Anonymous Authentication not initialised

I am using Spring4 with Spring Security 3.2.4.
I have some http configurations in my security configuration in order to host form based authentication and REST services (with authentication) together.
For the pages and REST services which require app-authentication everything works fine with my current configuration but for the pages which does not require authentication, such as login and register, the anonymous authentication is not initialised somehow. Speaking in Java:
SecurityContextHolder.getContext().getAuthentication() returns null.
I expect that anonymous authentication is initialised as the documentation (http://docs.spring.io/spring-security/site/docs/3.2.4.RELEASE/reference/htmlsingle/#introduction) refers:
Anonymous authentication support is provided automatically when using the HTTP configuration Spring Security 3.0 and can be customized (or disabled) using the element. You don’t need to configure the beans described here unless you are using traditional bean configuration.
Does anyone have an idea why does it not happen although the documentation refers? (Beside the fact, that the documentation for 3.2.4 refers to 3.0 version and some of the given configuration suggestions refer deprecated implementation)

Setting the Roles for #RolesAllowed in JAX-RS using jersey

I tried using Basic Authentication by changing the server.xml config of Tomcat 6.0 but it did not worked: BASIC authentication in jersey JAX-RS service and Tomcat 6.0 getting failed
Hence I am opting a way where no server specific config is needed and I can add up the roles directly in my code (either client or server; not sure about theavailable options).
Please provide me some ideas about the possible options for setting the user roles so that I can authenticate my Web Service methods using the #RolesAllowed annotation.
You need to go back and figure out why your security constraints weren't working. Maybe start with the default file realm before moving on to JDBC realms. #RolesAllowed in an annotation that triggers behavior in the container.
If you really want to do it yourself (a bad idea) they you'd probably start by creating a custom servlet filter that implemented the entire basic http challenge mechanism. Next you'd have to replace the SecurityContext provider in Jersey.
They "thing" that enables #RolesAllowed in jersey is this: http://java.net/projects/jersey/sources/svn/content/trunk/jersey/jersey-server/src/main/java/com/sun/jersey/api/container/filter/RolesAllowedResourceFilterFactory.java Which, by the way, don't forget to add as an init-param to your jersey servlet. The RolesAllowedResourceFilterFactory gets its security info from an injected SecurityContext which I'm sure at some point just delegates off to the Servlet API for credential info.
So basically if you don't want to take the time to get security constraints working you are going to end up replacing most of the chain...like I said, a bad idea.
The features on application servers are there to keep you from having to spend time creating infrastructure code, if you write your own infrastructure code you're going to have a bad time.

Categories

Resources