Nov 27, 2010

Chrome Behavior Confirmed, FF Checked With HSTS Add-On

Both Google and Mozilla have been very responsive in the discussion on HSTS behavior for non-default ports.

Chrome Behavior Confirmed
Adam Langley from Google confirmed that Chrome doesn't enforce HSTS for non-default ports:

From the write up you appear to be using a non-default HTTP port
(8080). That doesn't trigger rewriting in Chromium:

if (scheme == "http" &&
     (request->url().port().empty() || port == 80) &&
     request->context()->transport_security_state() &&
         &domain_state, request->url().host())) {

I understand that the draft may be updated to cover this case in the
future, in which case the code will also be updated.


Firefox Nightly Behavior Checked With HSTS Add-On
Daniel Veditz asked me to use Sid Stamm's Firefox add-on for HSTS:

I installed it, accessed to see that it showed up in the list which it did. But my site does not show up when I surf to it:

So we're now trying to find out if this is because of any of the following:
  • Non-default port 8443
  • Self-signed SSL certificate added as a permanent security exception in Firefox
  • Domain resolves to on my machine via /etc/hosts

Stay tuned :).

Nov 25, 2010

Still no HSTS in FF4 nightly or Chrome 7

This weekend i blogged about my proof-of-concept implementation of Strict-Transport-Security in Struts 2 and the fact that I didn't get the expected behavior in Firefox 4 nightly or Chrome 7, only in Firefox 3.6.12 + NoScript.

Mozilla have responded and cannot reproduce the problem/bug. So I set up the stuff again and really made sure I did it right.

The Problem
The problem is that the draft specification of HTTP Strict Transport Security (HSTS) doesn't make clear what should happen for non-default ports, i e 80 for HTTP and 443 for HTTPS. Unclear specs typically lead to undefined and different behavior between implementations.

For proper security HSTS should always switch from HTTP to HTTPS for HSTS-enabled sites within the max-age time space. After my blog post Andy Steingruebl addressed the issue on his blog.

My Setup
I've set my HSTS max-age to 30 seconds to be sure it won't time out. For each test I do the following:
  1. Make sure the browser at hand fully trusts my self-signed SSL cert for In the case of Chrome this means adding the cert to the Mac OS system keychain and activating SSL trust for it. In the case of Firefox it means adding a permanent security exception to the browser.
  2. Load to make sure it loads over HTTP.
  3. Load that should set the HSTS max-age to 30 seconds.
  4. Load to make sure it switches to HTTPS.
Still Works in FF 3.6 + NoScript
First I confirmed that Firefox 3.6.12 + NoScript still detects my strict-transport-security header and switches from HTTP to HTTPS. Se screenshot below. Sorry for the Swedish error message :), but in essence Firefox has switched to HTTPS without changing the port and my server simply doesn't respond with an HTTPS connection on that port.

Chrome 7 Does Not Switch to HTTPS
Performing the four test steps in Chrome 7.0.517.44 does not result in the browser switching to HTTPS. Instead it gladly loads over HTTP after having got the HSTS header. I tested this both by reloading a tab with the HTTP URL and by opening a new tab and entering the HTTP URL to make sure Chrome's sandboxing and one-process-per-tab was not the cause.

The screenshots below also show the HSTS header. First the HTTPS load:

... then the HTTP load:

Firefox 4.0b8pre (aka Minefield) Does Not Switch to HTTPS
The four test steps in the nightly build of Firefox 4 beta does not result in a switch to HTTPS either. See screenshots below.

First the HTTPS load:

... then the HTTP load:

I am always humble in these kind of cases. There might be errors or misunderstandings in my setup. Nevertheless, I think these test should work, at least to make me confident in Chrome's and Firefox's support for HSTS.

Nov 21, 2010

Strict-Transport-Security in Struts 2

One of the topics of the upcoming OWASP Global Summit is Browser Security and the new security features in the form of optional HTTP headers.
Is this the path towards enduser security in the era of web applications? Perhaps. Anyway, I did some proof-of-concept implementations to check out how they work and which browsers support them. In this blog post I'll cover the first one ...

HTTP Strict-Transport-Security
All the details are in the draft specification so I won't spend too much time explaining it here. Basically, it's about a response header like this:

Strict-Transport-Security: max-age=60; includeSubDomains

... where the max-age is specified in seconds and the includeSubDomains directive is optional. The header tells the browser to only accept or set up HTTPS connections with that domain for a number of seconds ahead. Further, the browser should not accept any kind of shortcomings of the SSL certificate presented by the server and should not let the user click through.

Strict-Transport-Security as a Struts 2 Interceptor
I implemented this as a Struts 2 interceptor:

import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.struts2.StrutsStatics;

import javax.servlet.http.HttpServletResponse;

public class StrictTransportSecurityInterceptor extends AbstractInterceptor {
    private static final Log logger = LogFactory.getLog(StrictTransportSecurityInterceptor.class);
    private static final String HSTS_HEADER = "Strict-Transport-Security";
    private static final String HSTS_VALUE_NAME = "max-age=";
    private static final int HSTS_VALUE_IN_SECONDS = 10;
    private static final String HSTS_VALUE_INCLUDE_SUBDOMAINS = "; includeSubDomains";

    public String intercept(ActionInvocation invocation) throws Exception {
        ActionContext context = invocation.getInvocationContext();
        HttpServletResponse response = (HttpServletResponse) context.get(StrutsStatics.HTTP_RESPONSE);
        String headerValue = HSTS_VALUE_NAME + HSTS_VALUE_IN_SECONDS;
        response.addHeader(HSTS_HEADER, headerValue);
        logger.debug("HSTS interceptor with policy: " + headerValue);
        return invocation.invoke();

And the interceptor can be used either directly (in struts.xml):

<package extends="struts-default" name="secureApp">
   <interceptor class="se.johnwilander.secureApp.strutsInterceptors.StrictTransportSecurityInterceptor" name="strictTransportSecurityInterceptor">

  <action class="se.johnwilander.secureApp.strutsActions.RegisterAction" name="register">
    <interceptor-ref name="strictTransportSecurityInterceptor"></interceptor-ref>
    <result name="success">/index.jsp</result>

... or be included in your custom Struts 2 interceptor stack.

Supported in Chrome 7, FF 3 + NoScript, and forthcoming FF 4
HSTS is only supported in Mozilla's and Google's browsers at the moment. But given the amount of attention around SSL problems and session hijacking lately (SSLStrip, Firesheep) I think Apple, Opera, and Microsoft will follow soon.

HSTS Draft Spec Doesn't Cover Non-Default Ports
When I tested the above interceptor on my local setup I could only get it to work in Firefox + NoScript, not in Chrome or Minefield (Firefox 4 beta). After contacting Mozilla (and Google) they explained that the specification is unclear on what the browser should do with non-default ports such as 8080 and 8443. Here's what Sid Stamm at Mozilla told me:

Hi John,

Basically, HSTS is not specified to do anything with non-default ports.

With regards to the Minefield implementation, kind of explains that non-default port handling is not addressed very well in the HSTS specification.  Non-standard ports are not changed by the "upgrade" performed by HSTS, but port 80 is changed to 443 (because 80 is default for HTTP and 443 is default for HTTPS).

The main use case that triggered the development of HSTS is that users don't usually type the scheme or port in address bar.  My reasoning for implementing it to ignore non-default ports is as follows:

  • If a user requests (by typing in address bar, following link or bookmark, etc) a specific port, they should get that port.
  • If the user doesn't type a port, they get the default port. In the case where no port _or_ scheme is typed, they currently get http on port 80 (which for HSTS hosts, is "upgraded").
  • If the scheme https is entered, HSTS is not needed.

I hope this helps. Basically, what I'm saying is that the behavior you noticed is intended. If you change the http server port to 80 and the https port to 443, HSTS should work as specified.

This of course makes it a little bit harder to test on your own machine. You'll have to set up some kind of  forwarding of port 80 to 8080 and 443 to 8443 so that the browser detects the switch from default https to default http.

MItM Attacks Possible By Abusing Non-Default Ports?
Making HSTS testing harder on your localhost is a minor problem compared to the possibility of circumventing the whole protection scheme by using non-default ports. Today SSLStrip just changes from default 443 to default 80 when it strips all https links, but it should be perfectly possible to change all those links to 8080 or the like. Client firewalls might refuse the request but I doubt it in the general case.

I would rather like HSTS to be effective for all port configurations but allow the enduser to configure it under the browser's preferences menu.

AppSec Manifesto

To start this blog off I want to publish my personal AppSec Manifesto.

AppSec Manifesto
  • Developers are not lazy, rather quality oriented ⇒ Insecure applications do not stem from laziness.
  • Features and functions are always more important than security ⇒ Security should enable more features and functions.
  • Responsible disclosure is a good way of achieving more secure applications ⇒ Hackers are needed.
  • Technology is a crucial part of security ⇒ Therefore I keep coding.

This manifesto will most probably evolve along with my future insights.