Re: [websec] HSTS: max-age=0 interacting with includeSubdomains

Tobias Gondrom <> Tue, 21 August 2012 09:31 UTC

Return-Path: <>
Received: from localhost (localhost []) by (Postfix) with ESMTP id D31EE21F86BD for <>; Tue, 21 Aug 2012 02:31:47 -0700 (PDT)
X-Virus-Scanned: amavisd-new at
X-Spam-Flag: NO
X-Spam-Score: -96.277
X-Spam-Status: No, score=-96.277 tagged_above=-999 required=5 tests=[AWL=-1.515, BAYES_00=-2.599, FH_HELO_EQ_D_D_D_D=1.597, FH_HOST_EQ_D_D_D_D=0.765, FM_DDDD_TIMES_2=1.999, HELO_DYNAMIC_IPADDR=2.426, HELO_EQ_DE=0.35, J_CHICKENPOX_33=0.6, RDNS_DYNAMIC=0.1, USER_IN_WHITELIST=-100]
Received: from ([]) by localhost ( []) (amavisd-new, port 10024) with ESMTP id QMZij1Y6JvBW for <>; Tue, 21 Aug 2012 02:31:47 -0700 (PDT)
Received: from ( []) by (Postfix) with ESMTP id D47E821F86C4 for <>; Tue, 21 Aug 2012 02:31:46 -0700 (PDT)
Received: (qmail 1374 invoked from network); 21 Aug 2012 11:31:44 +0200
Received: from (HELO ? ( by with ESMTPSA (DHE-RSA-AES256-SHA encrypted, authenticated); 21 Aug 2012 11:31:44 +0200
Message-ID: <>
Date: Tue, 21 Aug 2012 10:31:44 +0100
From: Tobias Gondrom <>
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:14.0) Gecko/20120714 Thunderbird/14.0
MIME-Version: 1.0
References: <> <> <>
In-Reply-To: <>
Content-Type: text/plain; charset="ISO-8859-1"; format="flowed"
Content-Transfer-Encoding: 7bit
Subject: Re: [websec] HSTS: max-age=0 interacting with includeSubdomains
X-Mailman-Version: 2.1.12
Precedence: list
List-Id: Web Application Security Minus Authentication and Transport <>
List-Unsubscribe: <>, <>
List-Archive: <>
List-Post: <>
List-Help: <>
List-Subscribe: <>, <>
X-List-Received-Date: Tue, 21 Aug 2012 09:31:48 -0000

On 21/08/12 01:19, Adam Barth wrote:
> On Mon, Aug 20, 2012 at 5:09 PM, Brian Smith <> wrote:
>> Adam Barth wrote:
>>> The way the implementation in Chrome works is that max-age=0 only
>>> clears the entry for that particular host name.  If there's another
>>> shorter host name with includeSubdomains, that isn't affected.
>> Let me try to clarify with an example, which I think should be explicitly documented in the spec:
>> 1. We visit and receive HSTS with max-age=1234567890 ; includeSubdomains
>> 2. We visit and receive HSTS with max-age=0
>> Now, is HSTS (because it inherits the HSTS setting from, or is it non-HSTS (because we received a max-age=0 for that host)?
> It is HSTS.  Visit (2) would have removed any
> entries in the HSTS state table, but there were none, so it didn't
> have any effect on the world.
>> When we receive a HSTS header from with max-age > 0, then we will treat the expiration time of to be MAX(expiration(, expiration(
> A simpler way to think about it is that you keep state for each host.
> To answer the question of whether a given host is HSTS, you walk the
> list of subdomains and check their unexpired state.
>> But, if we receive a HSTS header from with max-age == 0, there are two possibilities:
>> 1. We act consistently with the above case and calculate the HSTS expiration time of to be:
>>         MAX(expiration(, expiration(
>>      == MAX(expiration(, 0)
>>      == expiration(
>>     This means that we would effectively be ignoring the max=age == 0 value sent by
> Correct.
>> 2. We honor the max-age=0 directive sent by and turn off HSTS for, but not for That is, we would treat max-age == 0 from a subdomain as "do not do includeSubdomains inheritance for this subdomain."
> This is not correct.  We do "honor" the max-age directive set by
> by expiring any state.
> However, is HSTS due to state specific to,
> which is not expired.
>> Now, it seems to me that the only reasonable choice is #2, but the spec seems to imply that we should do #1.
> Choice #1 seems reasonable to me.
>> Here's the problematic scenerio:
>> 1. We visit and get the HSTS header with includeSubdomains and an (effectively) infinite expiration time.
> There is no such thing as an infinite expiration time.  Let's proceed
> assuming you mean "one year" rather than infinite.
>> 2. The owners of decides to turn of HSTS for whatever reason (perhaps the domain changed owners, or there's a compatibility issue, or whatever), so they start sending out HSTS with max-age=0 for and for all the subdomains.
> That's not a correct way of disabling HSTS after (1).  Instead, they
> need only send out an max-age=0 header for itself.
>> 3. We visit and get the max-age=0 HSTS header.
>> If we choose choice #2, we do what the owners of intended, by treating as non-HSTS right away, without ever needing to visit, which we might NEVER do ever again.
> I mean, it's just guesswork as to what they intend.  I can write a
> similar story in which the intent is the reverse.
>> If we choose choice #1, we will effectively be making HSTS forever, and the domain owner has no way to help us undo it.
> They can simply initiate a request to (e.g., by
> using an HTTP redirect or an HTML image element) and clear the HSTS
> state for that host name.
> Adam


Would agree with Adam.

And for Brian, I think there is actually one more use case that you 
haven't considered:
Look at it in reverse order:
1. We visit and receive HSTS with max-age=1234567890
2. We visit and receive HSTS with max-age=0 ; 

as far as I remember that would actually clear HSTS for
So your mathematical formula above would not reflect this correctly:
       MAX(expiration(, expiration(
     == MAX(expiration(, 0)
     == expiration(

Correct, or am I missing something here?

Best, Tobias

> _______________________________________________
> websec mailing list