Subject: [websec] Strict-Transport-Security syntax redux

From: "Ryan Sleevi" <[email-masked]>
Date: Sun, 2 Oct 2011 06:43:03 -0400

Following Julian Reschke's questions, I also had a few questions related
to the draft-02 syntax. I've been basing my understanding on RFC 5234,
which I understand to be the most current/relevant to understanding the
syntax.

First, maxAge and includeSubDomains both include value extension points,
defined as:

	maxAge     = "max-age"  OWS  "="  OWS  delta-seconds  [ OWS v-ext ]
	includeSubDomains =  "includeSubDomains"  [ OWS v-ext ]
	v-ext      = value     ; STS extension value

However, the rule for OWS is specified as:

	OWS       = *( [ CRLF ] WSP )

As written, it would seem that the OWS between either delta-seconds or
"includeSubDomains" can legitimately be omitted before the v-ext. As best
I can tell, this would mean that the following values would still be
valid:

	max-age=123.456
	includeSubDomainsabc

In the first case ".456" is the v-ext, while in the second, "abc" is. In
both cases, because the OWS is truly optional (valid to have 0
occurrences), only the v-ext is present. For maxAge in particular, this
can lead to very silly parsing interpretations. Considering the following
value:

	max-age=123

If I'm not mistaken, ABNF doesn't specify any sort of greedy matching, so
this could be interpreted as:
	delta-seconds = 1 (1DIGIT), v-ext = 23 (0OWS 2tchar)
	delta-seconds = 12 (2DIGIT), v-ext = 3 (0OWS 1tchar)

To remedy this, I believe some form of explicit delimiter between the
existing values and the v-ext should be defined for the existing headers.
If the intent was to have extension values whitespace separated, then
would the following modification to introduce required whitespace solve
it?

	maxAge     = "max-age"  OWS  "="  OWS  delta-seconds  [ RWS v-ext ]
	includeSubDomains =  "includeSubDomains"  [ RWS v-ext ]
	v-ext      = value     ; STS extension value
	OWS       = *( [ CRLF ] WSP )
	RWS       = 1*( [ CRLF ] WSP )

Alternatively, can/should the v-ext be dropped entirely, and extensions to
the STS header be accomplished via defining a new STS-d-ext?

Second, as currently specified, extension directives take the form of:

	STS-d      = STS-d-cur / STS-d-ext
	STS-d-ext  = name      ; STS extension directive
	name       = token
	token      = 1*tchar
	tchar      = "!" / "#" / "$" / "%" / "&" / "'" / "*"
      	     / "+" / "-" / "." / "^" / "_" / "`" / "|" / "~"
	           / DIGIT / ALPHA

As currently written, STS-d-ext doesn't seem to be able to contain name
"=" value pairs such as those used by Evans' and Palmer's pinning draft,
because "=" is not a valid token character. Likewise, it wouldn't be able
to contain "#rule" style lists, because comma is not a valid tchar.

Should STS-d-ext be defined as "value" instead, so that it contain a wider
range of characters? Alternatively, if the intent for STS-d-ext was that
it would always include some name, should it be defined as "name [ OWS "="
OWS value ]"?

Either way seems to offer a solution. It seems that if a parser were
written based on the current draft, it would be correct/valid to reject an
STS header that included cert pins, as specified in the current pinning
draft. This would be because the cert pinning draft makes use of "="
within the extension definitions, which is not a legal character for an
STS-d-ext in the base spec.

Best regards,
	Ryan