How to downshift from HTTPS to HTTP in your web application

How to downshift from HTTPS to HTTP in your web application

How to downshift from HTTPS to HTTP in your web application

Problem description

Web applications will normally protect any of their web resources that process security-sensitive information (e.g., the login page used by FORM authentication) with SSL, to ensure that the information submitted by the user (e.g., username and password security credentials) will not be transmitted in the clear.

Some web applications will only protect web resources that process security-sensitive information in this manner, while allowing requests for other resources to travel in the clear.

Imagine a user accessing some of the unprotected resources of a web application (over HTTP) before accessing a resource that requires FORM authentication, where the FORM login page is guarded by a transport guarantee of CONFIDENTIAL according to the web application's deployment descriptor. In this case, the protocol will change from HTTP to HTTPS (to satisfy the transport guarantee of the login page), and will remain HTTPS even after the user has authenticated successfully and continues accessing any of the unprotected resources.

The fact that the protocol remains HTTPS once it has been changed to HTTPS, even though HTTPS may no longer be required, can be a problem for performance sensitive applications that do not want to incur the overhead of SSL unless required.

This blog explains how a web application can be written so that the protocol changes back to HTTP when HTTPS is no longer needed.

Solution

Imagine a very simple web application containing two servlets UseHttp.java and UseHttps.java, which are mapped to /useHttp and /useHttps, respectively. Further assume that the web application must satisfy the following requirements:

  • REQ_1: Access to "/useHttps" must be over HTTPS (otherwise, the servlet will throw an exception). This means that if the current protocol is HTTP, it must be upgraded to HTTPS.
  • REQ_2: Access to "/useHttp" must be over HTTP (otherwise, the servlet will throw an exception). This means that if the current protocol is HTTPS, it must be downshifted to HTTP.

REQ_1 will be handled automatically by the container, by declaring the following security constraint in the web application's web.xml:

  <security-constraint>
    <web-resource-collection>
      <web-resource-name>Protected resource</web-resource-name>
       <url-pattern>/useHttps</url-pattern>
       <http-method>GET</http-method>
    </web-resource-collection>
    <user-data-constraint>
      <transport-guarantee>CONFIDENTIAL</transport-guarantee>
    </user-data-constraint>
  </security-constraint>

REQ_2 cannot be handled automatically by the container, and must be implemented by the application itself. In order to address REQ_2, we are going to implement a filter that intercepts every request and determines programmtically whether the target resource is guarded by a transport guarantee of CONFIDENTIAL. If it is not, and the request came in over HTTPS, the filter will issue a redirect to the target resource over HTTP.

In order to determine if a requested resource is guarded by HTTPS, the filter leverages the JavaTM Authorization Contract for Containers (Java ACC), which defines new java.security.Permission classes to satisfy the authorization model of the Java Platform, Enterprise Edition. In our particular case, the filter creates and uses an instance of javax.security.jacc.WebUserDataPermission to check if the target resource of a request is guarded by HTTPS.

The complete source code of the filter and the two servlets, as well as the web.xml, are provided as part of the sample application's WAR file and may be downloaded from this link.

For the sake of simplicity, we assume the default GlassFish configuration of three HTTP listeners: one listener reserved for any admin requests, a second listener for user traffic over HTTP (on port 8080), and a third listener for user traffic over HTTPS (on port 8181). (Notice that when you are using GlassFish v3 Prelude, you must enable the HTTPS listener on port 8181, which is disabled by default.)

The port number of the HTTP port for user requests is fed into the filter via an init parameter in web.xml:

  <filter>
    <filter-name>MyFilter</filter-name>
    <filter-class>MyFilter</filter-class>
    <init-param>
      <param-name>httpPort</param-name>
      <param-value>8080</param-value>
    </init-param>
  </filter>

This is necessary so that the filter will know which port number to use when redirecting from HTTPS to HTTP.

Demo

To see the upshift from HTTP to HTTPS, and downshift from HTTPS to HTTP in action, follow these simple steps:

  1. Download and install GlassFish v2 UR2 or GlassFish v3 Prelude.
  2. Download the sample web application sample.war, and deploy it to GlassFish.
  3. Access this URL: http://leah:8080/sample/useHttps. Notice how the request gets redirected to the HTTPS port, and how the protocol changes from HTTP to HTTPS: https://localhost:8181/sample/useHttps.
  4. Now edit the above HTTPS URL in your browser's address window by removing the trailing s from useHttps, so that the URL reads: https://localhost:8181/sample/useHttp, and hit return. Notice how the browser displays this warning message: You are about to leave an encrypted page. Information you send or receive from now on could easily be read by a third party, before issuing a redirect to the HTTP port and downshifting the protocol from HTTPS to HTTP: http://localhost:8080/sample/useHttp

I hope you found this blog useful. Please send any questions or comments you may have to the GlassFish webtier alias, or post them to the GlassFish webtier forum. I also would like to thank GlassFish security architect Ron Monzillo for his Java ACC related contribution to the idea behind this blog and to the filter code of the sample web application.

Comments:

A useful tip. I wish the filter code were inlined in the blog along with some explanation.

Posted by sahoo on December 08, 2008 at 02:59 PM PST #

If you want to avoid the browser warning you can do the change to HTTP through a client-side redirect.

But it's important to remind people that mixing protocols is almost as bad as just using HTTP, if your session cookie goes over the wire unencrypted it can be stolen by anyone snooping and they can easily impersonate your session.

Posted by Colin on December 08, 2008 at 09:07 PM PST #

From a security perspective, this is terrible advice.
Switching back to HTTP from HTTPS, is almost as bad as sending your password unencrypted over the wire to begin with.
Once it HTTPS, you should really set a new set of cookies, and then NOT switch back to HTTP

Posted by Filip Hanik on December 09, 2008 at 11:06 AM PST #

Hello Jan,
I'm working currently on a GFV3Prelude deployment of a grails application. I have spent the past day or two working on this exact same issue. My approach is somewhat different, for the most part my app is agnostic of http to https.
I am using apache to automatically forward http to https at context points that require https via mod_rewrite. I had been looking for a way to do this with glassfish, as glassfish offers similar functionality, but I found it underdocumented for my needs.
In addition to use mod rewrite I plan on adding the secure points to my web-app using arguments in the web.xml. Preliminary testing of this setup looks favorable and seems to have less impact on the codebase? I just thought I would mention in case you had any comments or suggestions.

Posted by Daniel Honig on December 09, 2008 at 02:20 PM PST #

see the upshift from HTTP to HTTPS, and yes........

Posted by yemek oyunları on December 19, 2008 at 08:09 AM PST #

Is there some sort of trick to get this down-shifting to work on Websphere. I deployed the WAR on Glassfish, and the HTTP/HTTPS transition works fine, in both directions. However, the same WAR, deployed on Websphere 7 does not work completely. Specifically, the line that determines if the URL is supposed to be protected by SSL:

boolean isTransportProtected = policy.implies(pd, p) ? false : true;

Always returns false. So if I go to a page that has a CONFIDENTIAL transport guarantee, a redirect will be issued, correctly up-shifting to HTTPS. However, the filter cannot tell that the page is supposed to be protected (as mentioned isTransportProtected always comes back as false), and so the filter tries to downshift back to HTTP. This ends up causing an infinite loop of redirects which my browser ultimately recognizes and returns an error.

No errors or exceptions are thrown during the execution of the filter. Can anyone offer a suggestion as to why this might be the case?

Thanks in advance for any help.

Posted by Chris Jarabek on August 19, 2010 at 03:15 AM PDT #

Post a Comment:
  • HTML Syntax: NOT allowed
About

jluehe

Search

Categories
Archives
« July 2015
SunMonTueWedThuFriSat
   
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
 
       
Today