1

I am new to the Spring Framework, and I have searched through every possible SO link related to this topic but could find any proper solution. I have an application running on JBoss Wildfly. It sits on an AWS EC2 instance, which sits behind an Elastic Load Balancer(ELB). The ELB has SSL configured in it so that it only accepts HTTPS requests from clients, my application server however communicates with the ELB only through HTTP. My application uses both Spring MVC and Spring Security for front-end/security management. My security-context.xml file is as follows:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:security="http://www.springframework.org/schema/security"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.2.xsd
    http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd">

<context:property-placeholder location="/WEB-INF/config.properties" />
<context:annotation-config></context:annotation-config>
<context:component-scan base-package="com.etech.security">          </context:component-scan>
<security:authentication-manager>
    <security:authentication-provider>
        <security:user-service>
            <security:user name="xxx@xxxx.com"
                authorities="admin" password="xxxxxxxx" />
        </security:user-service>
</security:authentication-provider>
</security:authentication-manager>
<security:http use-expressions="true">
    <security:intercept-url pattern="/" access="isAuthenticated()" />
    <security:intercept-url pattern="/dashboard"
        access="isAuthenticated()" />
    <security:intercept-url pattern="/resources/**" access="permitAll" />
    <security:intercept-url pattern="/login" access="permitAll" />
    <security:intercept-url pattern="/**" access="denyAll" />
    <security:form-login login-page="/login"
        authentication-success-handler-ref="asyncAuthenticationSuccessHandler"
        authentication-failure-handler-ref="asyncAuthenticationFailureHandler" />
    <security:logout logout-url="/logout" logout-success-url="/" />

</security:http>
<bean id="asyncAuthenticationSuccessHandler"
    class="com.etech.security.AsyncAuthenticationSuccessHandler">
<constructor-arg ref="supportMailPassword"></constructor-arg>
</bean>

The problem with this setup is, once authenticated the application returns a HTTP link to the client, which the client cannot access as the ELB only allows HTTPS urls. This is shown below:

Client --HTTPS---> ELB ---HTTP--> AppServer/Application

Client <--HTTP-- ELB <---HTTP--- AppServer/Application

But it should actually do this,

Client --HTTPS---> ELB ---HTTP--> AppServer/Application

Client <--HTTPS-- ELB <---HTTPS--- AppServer/Application

What is the right approach to this, do I need to use any kind of Filter that would bind to the Response after authentication/processing, where I can change the response url to HTTPS?

Gab
  • 7,869
  • 4
  • 37
  • 68
rahsan
  • 170
  • 9
  • Did you try this solution http://stackoverflow.com/questions/8002272/offloading-https-to-load-balancers-with-spring-security? – shazin Apr 06 '15 at 01:24
  • I tried that solution but for some reason the InsecureChannelProcessor could not detect "X-Forwarded-Proto" header from ELB, but the SecureChannelProcessor could. I bypassed the header checking in Insecure to commence all traffic through a secure channel but it was still sending http urls back to the client. – rahsan Apr 06 '15 at 06:32

1 Answers1

1

After digging into the problem a little bit more I found out this was mainly due to the Spring Dispatcher Servlet detecting inbound connections as insecure, that is all connections were through HTTP, this would cause all outbound connections to be through HTTP as well.

Spring would detect this through the following method call: HttpServletRequest.isSecure(), this will always return false as the connection from ELB to Application Server is through HTTP and not HTTPS.

There is a nice blog post describing this exact same problem, and posting a simple solution that I used myself.

The solution is to use a filter that would be placed at the top of the filter chain. It would intercept all incoming request, replace the inbound HttpServletRequest with a custom HttpServletRequest, that will have the isSecure() method returning true. Spring dispatcher Servlet would then detect this and send HTTPS responses back to the client.

The steps are simple:

  1. Create a custom HttpServletRequestWrapper

    public class CustomWrapper extends HttpServletRequestWrapper
    {
    
        public CustomWrapper(HttpServletRequest request) {
            super(request);
    
        }
    
       public boolean isSecure()
       {
            return true;
       }
    
        public String getScheme()
       {
           return "https";
       }
    }
    
  2. Create a Filter that would user this wrapper

    public class CustomFilter implements Filter 
    {
    
        @Override
        public void destroy() {
    
        }
    
        @Override
        public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain) throws IOException, ServletException 
        {
            CustomWrapper wrapper = new CustomWrapper((HttpServletRequest) request);
            chain.doFilter(wrapper, response);
    
        }
    
        @Override
        public void init(FilterConfig arg0) throws ServletException {
    
       }  
    

    }

  3. In web.xml add this filter before any other filter, including Spring Security Filter chain.

rahsan
  • 170
  • 9