Subject: [websec] Comments on draft-ietf-websec-key-pinning-06

From: David Matson <[email-masked]>
Date: Thu, 20 Jun 2013 18:55:17 +0000

I sent the mail below to the draft-ietf-websec-key-pinning-06 authors, and Chris Palmer suggested I raise the points on this mailing list. He also mentioned a previous discussion (which I haven't been able to locate) around a well-known host security information URL; if there's a good place to get up to speed on that discussion, a pointer would be much appreciated.

Thanks,

David

I really like the overall approach taken in Public Key Pinning Extension for HTTP (draft-ietf-websec-key-pinning-06). Particularly, the public key (including algorithm) seems like exactly the right thing to pin.

A couple of thoughts on other areas of the draft (my apologies if these have been discussed before elsewhere; I'm only familiar with the draft text):

1. It would be nice to avoid including full public key data with every HTTP response. Particularly if there are multiple public keys in use, there's a bit of potential data overhead on every response. Would it make more sense to have the public key data in a separate resource? As a first step, perhaps having every resource just have a pointer, using a header something like the following:
Public-Key-Pin-Location: /pins
Where "/pins" would be a separate resource that would have Public-Key-Pins header data.
Or, to go one step further, this data could then appear in the entity-body (perhaps in JSON format) and use normal HTTP resource-level mechanisms-for example, using Cache-Control to specify the expiration rather than a custom max-age header directive.

2. It would be nice to have the public key specified in a central place for the host, rather than by any resource, since the public keys are at the TLS level and don't vary per resource. I'm not sure there's an ideal solution here, but a couple of options might be:
a. Have a fixed, well-known resource per host for retrieving public key pin data (such as "/well-known-pins" or something like that).
b. An OPTIONS * request might be a good fit here, it was apparently intended for server-level information. Perhaps the response to an OPTIONS * could have the public key data (like the Public-Key-Pins header). Alternatively, to combine the two ideas, the OPTIONS * response could, instead of having the public key data itself, instead have a header with a URL for a public key pins resource (like the Public-Key-Pin-Location header mentioned above).

Thanks for considering these ideas.

---

From: Yoav Nir <[email-masked]>
Date: Sun, 23 Jun 2013 07:25:58 +0000

Hi David

As far as I know, this idea was not discussed before. If we were to do this, the proper URI for this would be some kind of RFC 5785 URI like "/.well-known/pins" or "/.well-known/hpkp".

Looking at the examples in the key-pinning draft, an HPKP header using SHA-1 takes just under 120 bytes. Compared to some of the stuff that gets sent in HTTP headers (I'm looking at you, user-agent) this is pretty tame. Moreover, the key-pinning header does not have to be sent in every response - it's enough to send it once per full TLS handshake.  Another think to consider is HTTP/2, where the header compression is not yet specified, but the algorithm that is currently being discussed on the mailing list has the ability to reference headers from previous responses, so that the full Public-Key-Pin could be included only once, and referred back to in other responses, although that's not really necessary.

Still, I can see several merits in your proposal:
 - clients that don't support key pinning need not get a header they're not going to process
 - Inserting a header only in the first response requires information from the TLS layer, so it's easier to just insert the header in every response.
 - HTTP/2 is not yet here, let alone wide-spread.

What do others think?

Yoav

On Jun 20, 2013, at 9:55 PM, David Matson <[email-masked]<mailto:[email-masked]>> wrote:

I sent the mail below to the draft-ietf-websec-key-pinning-06 authors, and Chris Palmer suggested I raise the points on this mailing list. He also mentioned a previous discussion (which I haven’t been able to locate) around a well-known host security information URL; if there’s a good place to get up to speed on that discussion, a pointer would be much appreciated.

Thanks,

David

A couple of thoughts on other areas of the draft (my apologies if these have been discussed before elsewhere; I’m only familiar with the draft text):

1. It would be nice to avoid including full public key data with every HTTP response. Particularly if there are multiple public keys in use, there’s a bit of potential data overhead on every response. Would it make more sense to have the public key data in a separate resource? As a first step, perhaps having every resource just have a pointer, using a header something like the following:
Public-Key-Pin-Location: /pins
Where “/pins” would be a separate resource that would have Public-Key-Pins header data.
Or, to go one step further, this data could then appear in the entity-body (perhaps in JSON format) and use normal HTTP resource-level mechanisms—for example, using Cache-Control to specify the expiration rather than a custom max-age header directive.

2. It would be nice to have the public key specified in a central place for the host, rather than by any resource, since the public keys are at the TLS level and don’t vary per resource. I’m not sure there’s an ideal solution here, but a couple of options might be:
a. Have a fixed, well-known resource per host for retrieving public key pin data (such as “/well-known-pins” or something like that).
b. An OPTIONS * request might be a good fit here, it was apparently intended for server-level information. Perhaps the response to an OPTIONS * could have the public key data (like the Public-Key-Pins header). Alternatively, to combine the two ideas, the OPTIONS * response could, instead of having the public key data itself, instead have a header with a URL for a public key pins resource (like the Public-Key-Pin-Location header mentioned above).

Email secured by Check Point

_______________________________________________
websec mailing list
[email-masked]<mailto:[email-masked]>
https://www.ietf.org/mailman/listinfo/websec

---

From: Trevor Perrin <[email-masked]>
Date: Sun, 23 Jun 2013 18:13:22 -0700

On Sun, Jun 23, 2013 at 12:25 AM, Yoav Nir <[email-masked]> wrote:

>  Hi David
>
>  As far as I know, this idea was not discussed before. If we were to do
> this, the proper URI for this would be some kind of RFC 5785 URI like
> "/.well-known/pins" or "/.well-known/hpkp".
>
>  Looking at the examples in the key-pinning draft, an HPKP header using
> SHA-1 takes just under 120 bytes.
>

Depends on the number of pinned keys.  Chrome's existing preloads [1] have
9, 5, 19, 36, 2, and 2 keys.  That's a mean of 12, which would be >500
bytes with SHA256.

>  Compared to some of the stuff that gets sent in HTTP headers (I'm looking
> at you, user-agent) this is pretty tame. Moreover, the key-pinning header
> does not have to be sent in every response - it's enough to send it once
> per full TLS handshake.
>

I thought so too, but draft-04 and later say:

"If the Pinned Host does not include the PKP header field, and if the
connection passed Pin Validation, UAs MUST treat the host as if it had set
its max-age to 0 (see Section 2.3.1)."

So apparently it *does* need to be sent on every response, to maintain the
pin?

Still, I can see several merits in your proposal:
>  - Inserting a header only in the first response requires information from
> the TLS layer, so it's easier to just insert the header in every response.

Agreed it has merit, particularly if there's any possibility of future
pinning policies growing larger with other types of data (e.g. policies for
error-handling and reporting, or "pinning" to other things like CT, DNSSEC,
TACK, etc...)

Trevor

---

From: Tobias Gondrom <[email-masked]>
Date: Mon, 24 Jun 2013 15:01:32 +0800

Hi all,

<no hat>
comments inline.
Best regards, Tobias

On 24/06/13 09:13, Trevor Perrin wrote:
>
> On Sun, Jun 23, 2013 at 12:25 AM, Yoav Nir <[email-masked]
> <mailto:[email-masked]>> wrote:
>
>     Hi David
>
>     As far as I know, this idea was not discussed before. If we were
>     to do this, the proper URI for this would be some kind of RFC 5785
>     URI like "/.well-known/pins" or "/.well-known/hpkp".
>
>     Looking at the examples in the key-pinning draft, an HPKP header
>     using SHA-1 takes just under 120 bytes.
>
>
> Depends on the number of pinned keys.  Chrome's existing preloads [1]
> have 9, 5, 19, 36, 2, and 2 keys.  That's a mean of 12, which would be
> >500 bytes with SHA256.
>

IMHO the expected session will be 2 keys with SHA-160 to SHA-512 per host.
Just for my understanding: Do you think this will create too much header
overhead and there want to the reference to the pin?

>  
>
>     Compared to some of the stuff that gets sent in HTTP headers (I'm
>     looking at you, user-agent) this is pretty tame. Moreover, the
>     key-pinning header does not have to be sent in every response -
>     it's enough to send it once per full TLS handshake.
>
>
> connection passed Pin Validation, UAs MUST treat the host as if it had
> set its max-age to 0 (see Section 2.3.1)."
>
> So apparently it *does* need to be sent on every response, to maintain
> the pin?

Yes. That is my reading of the paragraph, too. I guess the assumption
was that this shall function as a fail safe instead of actively setting
it to value 0.

>      - Inserting a header only in the first response requires
>     information from the TLS layer, so it's easier to just insert the
>     header in every response.
> pinning policies growing larger with other types of data (e.g.
> policies for error-handling and reporting, or "pinning" to other
> things like CT, DNSSEC, TACK, etc...)
>
>
> Trevor
>
>
> [email-masked]

---

From: Trevor Perrin <[email-masked]>
Date: Mon, 24 Jun 2013 14:06:13 -0700

On Mon, Jun 24, 2013 at 12:01 AM, Tobias Gondrom <[email-masked]
> wrote:

> have 9, 5, 19, 36, 2, and 2 keys.  That's a mean of 12, which would be >500
> bytes with SHA256.
>

I don't know what the common case would be.  The numbers I cited are from
the Chromium's preloaded key pins:

https://src.chromium.org/viewvc/chrome/trunk/src/net/http/transport_security_state_static.json

Note that a commercial CA might have several subordinate and root
certificates, with different keys.  To pin yourself to this CA, you may
need to list several of these keys, and to be conservative a site might pin
multiple CAs.

(Are you suggesting that sites would list both a SHA256 and SHA1 hash for
each pinned key?  That seems unnecessary.)

> overhead and there want to the reference to the pin?

I don't know how much data a site could add to every HTTP Response before
it starts becoming a problem.  But that's the issue David raised.

If it's a real problem, the idea of putting pin assertions in, say, a JSON
file at a "well-known" URI seems reasonable.  E.g.:

http://www.example.com/.well-known/public-key-pins

Trevor

---

From: Chris Palmer <[email-masked]>
Date: Mon, 24 Jun 2013 14:21:29 -0700

On Sun, Jun 23, 2013 at 12:25 AM, Yoav Nir <[email-masked]> wrote:

> As far as I know, this idea was not discussed before. If we were to do this,
> the proper URI for this would be some kind of RFC 5785 URI like

I think it has been brought up in another forum; Ryan Sleevi will know
better than I.

> Looking at the examples in the key-pinning draft, an HPKP header using SHA-1
> takes just under 120 bytes. Compared to some of the stuff that gets sent in
> HTTP headers (I'm looking at you, user-agent) this is pretty tame.

Not to mention the body. :)

> Moreover,
> the key-pinning header does not have to be sent in every response - it's
> enough to send it once per full TLS handshake.

Actually, we ended up changing that, a while back now. From the current draft:

"""
2.2.1. HTTP-over-Secure-Transport Request Type

   When replying to an HTTP request that was conveyed over a secure
   transport, a Pinned Host SHOULD include in its response exactly one
   PKP header field that MUST satisfy the grammar specified above in
   Section 2.1.  If the Pinned Host does not include the PKP header
   field, and if the connection passed Pin Validation, UAs MUST treat
   the host as if it had set its max-age to 0 (see Section 2.3.1).
"""

In most realistic web application scenarios, even sending an HPKP
header on every response is likely to have negligible impact on
overall application performance. Almost every web application I have
seen has much lower-hanging fruit for performance optimization.

---

From: Chris Palmer <[email-masked]>
Date: Mon, 24 Jun 2013 14:29:36 -0700

If you haven't already, I'd urge everyone to take pcaps of a web
session to their bank or to their web mail provider or whatever. I
think you'll quickly see that even a large HPKP header, say 500 bytes,
is not going to be the thing that makes web traffic bloated.
(Sometimes, the certificate chains themselves outweigh the pins — and
that traffic occurs before the crucial point of widening the TCP
window! Whereas HTTP headers most likely occur afterward.)

Also, at one point somebody raised an idea of saying you could pin to
a set of keys — say, Symantec or StartCom's issuing certs — with a
single directive. Something like:

    Public-Key-Pins: max-age=...; pin-set: symantec; includeSubDomains

There'd need to be some kind of registry for the names of sets, of
course, which is complicated. And how do UAs learn of updates to the
sets, and so on. It's a nice idea that would improve on-the-wire size
in bytes, and also enable web application providers to pin more
easily. If there is demand, perhaps we could create such an extension
to HPKP/TACK/et c. But I don't think it should be a blocker for this
I-D.

---

From: Tobias Gondrom <[email-masked]>
Date: Tue, 25 Jun 2013 09:30:22 +0800

On 25/06/13 05:06, Trevor Perrin wrote:
>
> On Mon, Jun 24, 2013 at 12:01 AM, Tobias Gondrom
> <[email-masked] <mailto:[email-masked]>> wrote:
>
>
>>     Depends on the number of pinned keys.  Chrome's existing preloads
>>     [1] have 9, 5, 19, 36, 2, and 2 keys.  That's a mean of 12, which
>>     would be >500 bytes with SHA256.
>>
> I don't know what the common case would be.  The numbers I cited are
> from the Chromium's preloaded key pins:
>
> certificates, with different keys.  To pin yourself to this CA, you
> may need to list several of these keys, and to be conservative a site
> might pin multiple CAs.
>
> (Are you suggesting that sites would list both a SHA256 and SHA1 hash
> for each pinned key?  That seems unnecessary.)

No I am not suggesting to send both hashes. That would be unnecessary
and of very use.
So that means: 2 keys (the main key and the backup key) with one hash
each (SHA-160 OR SHA-256 OR SHA-512 OR ...)
Looking back at the discussions my perception is that in the case of
many (though not all) implementations the server will have only one key
active at the point of entry and then use a reverse proxy.
(with a caveat: I remember one large bank that seems due to whatever
historic reason is actually using different certificates for every
singly server instance, which obviously could then go into the 100s or
1000s.... - but I don't worry about this too much as AFAIK this was more
duet to misunderstanding regulation and a stupid architecture - so will
just need to clean up before they can go to pinning - which is fair in
my eyes.)

>
>  
>
>     Just for my understanding: Do you think this will create too much
>     header overhead and there want to the reference to the pin?
>
>
> I don't know how much data a site could add to every HTTP Response
> before it starts becoming a problem.  But that's the issue David raised.  
>
> If it's a real problem, the idea of putting pin assertions in, say, a
> JSON file at a "well-known" URI seems reasonable.  E.g.:
>

I see. From my humble experience, I would agree with Chris (in his later
email) and don't see a performance problem with this. So I don't think
we need to consider the URI option.

Best regards, Tobias

---

From: Trevor Perrin <[email-masked]>
Date: Mon, 24 Jun 2013 19:23:43 -0700

On Mon, Jun 24, 2013 at 6:30 PM, Tobias Gondrom
<[email-masked]>wrote:

>  On Mon, Jun 24, 2013 at 12:01 AM, Tobias Gondrom <
> [email-masked]> wrote:
>
>>
> No I am not suggesting to send both hashes. That would be unnecessary and
> of very use.
> So that means: 2 keys (the main key and the backup key) with one hash each
> (SHA-160 OR SHA-256 OR SHA-512 OR ...)
>

You're ignoring pinning to CAs, which is a major use case for HPKP.

>  I remember one large bank that seems due to whatever historic reason is
> actually using different certificates for every singly server instance,
> which obviously could then go into the 100s or 1000s.... - but I don't
> worry about this too much as AFAIK this was more duet to misunderstanding
> regulation and a stupid architecture - so will just need to clean up before
> they can go to pinning - which is fair in my eyes.)
>

A single hostname could be using different SSL keys at the same time for
various reasons, e.g. in-progress key changes, geographically distributed
servers, HSM limitations, CDNs, etc.

So you can't ignore this case quite so easily.  It's one of the reasons I
prefer pinning signing keys to pinning SSL keys.

> email) and don't see a performance problem with this. So I don't think we
> need to consider the URI option.
>

OK.

Trevor

---

From: Trevor Perrin <[email-masked]>
Date: Tue, 25 Jun 2013 17:13:49 -0700

>     Public-Key-Pins: max-age=...; pin-set: symantec; includeSubDomains

I like that!  It would be a lot easier to list your pin as
"symantec,comodo,godaddy" than the equivalent set of keys, particularly
since keys per CA change over time.

> sets, and so on.

It seems like a registry IANA could maintain.  Any CA in a major root store
could register their name and a URL where they keep the list of keys
necessary to pin them.  Browser vendors will download these key lists (over
pinned HTTPS, of course) and push updates to browsers on some regular
basis.

Seems pretty workable...

> It's a nice idea that would improve on-the-wire size

TACK wouldn't do this, we're focused on self-chosen signing keys.  But it
would be a great v2 feature for HPKP, in my opinion...

Trevor

---

From: Tobias Gondrom <[email-masked]>
Date: Fri, 28 Jun 2013 15:23:05 +0800

<no hats>

On 26/06/13 08:13, Trevor Perrin wrote:
>
> On Mon, Jun 24, 2013 at 2:29 PM, Chris Palmer <[email-masked]
> <mailto:[email-masked]>> wrote:
>
>     (Sometimes, the certificate chains themselves outweigh the pins
>     --- and
>     a set of keys --- say, Symantec or StartCom's issuing certs --- with a
> "symantec,comodo,godaddy" than the equivalent set of keys,
> particularly since keys per CA change over time.
>
>
>  
>
> It seems like a registry IANA could maintain.  Any CA in a major root
> store could register their name and a URL where they keep the list of
> keys necessary to pin them.  Browser vendors will download these key
> lists (over pinned HTTPS, of course) and push updates to browsers on
> some regular basis.  
>
> Seems pretty workable...

Actually it's a little it more complicated. This list seems pretty
dynamic and may also need authentication and authorization before you
add more keys to sets?
Both things that IANA would not be very good for...
Or am I missing something and this is easier?

> TACK wouldn't do this, we're focused on self-chosen signing keys.  But
> it would be a great v2 feature for HPKP, in my opinion...
>
>
> Trevor
>   
>
>

---

From: Phillip Hallam-Baker <[email-masked]>
Date: Fri, 28 Jun 2013 11:00:56 -0400

CAA faced the problem of identifying a CA.

During the evolution of the draft we went through pretty much every scheme
mentioned in this thread. In the end we decided to go with a domain name
that is asserted for that purpose by the CA. So symantec.com / comodo.com /
etc.

At this point the main reason Cert Pinning should follow the same route is
that it is already in use and absent a good reason, TLS CA pinning should
follow the existing approach. This makes it more likely that we will get
the support embedded into platforms accurately.

The main reason for going with domain names in CAA was that it means there
is no need to create a new registry. That is important as it feels wrong to
have an IETF spec differentiate between commercial and private CAs. It
feels even wronger to have IANA managing registries of commercial
providers. There are good business reasons for the CAs to not want their
business under the control of IANA and vice versa.

There are several other advantages though. One is that it makes it much
easier to work out how to put in mechanisms to 'unpin' since these can now
key off Domain names.

So hypothetically let us imagine that there was a Diginotar situation and
several hundred thousand browsers are now pinned to the busted root. Ooops.

One way that we could approach unpinning in this situation would be to use
something involving DNSSEC

Now that is not an argument for writing more code now. But putting DNS
names there are at least as good as any other option but the fact that they
are a discovery index provides a LOT of power that may be useful down the
line.

This might happen in CAA. An obvious next step for CAA is to have a cert
request mechanism where the end entity looks at the CAA record to find out
where it can get certs from.

---

From: Trevor Perrin <[email-masked]>
Date: Fri, 28 Jun 2013 13:32:43 -0700

On Fri, Jun 28, 2013 at 8:00 AM, Phillip Hallam-Baker <[email-masked]>wrote:

> that is asserted for that purpose by the CA. So symantec.com / comodo.com/ etc.
>

Makes sense.

How do CAs assert the domain name they'd like to be referenced by?  Are
these domain names something that could be tracked by the CAB Forum,
browser root stores, or some other party?

HPKP still needs to map the declared domain name to a set of keys.  Perhaps
CAs could maintain a list at a "well-known" URI derived from the domain
name?

https://comodo.com/.well-known/hpkp-keys.json

Browser vendors could scan this list periodically and keep their browsers
in sync with the latest keys from the major CAs.  CAs would make sure to
publish new keys in advance of issuing certs under a new root.

If a browser encounters an unknown domain name, it could contact the URI
itself, so this doesn't disenfanchise private CAs.

Anyways, I rather like this.  I think it's a much easier route to CA
pinning than expecting websites to maintain key lists themselves.

Others?

Trevor

---

From: Phillip Hallam-Baker <[email-masked]>
Date: Sun, 30 Jun 2013 13:53:26 -0400

CAA does not require a central registry. But it does require CAs to decide
what DNS name(s) they are going to use.

For key pinning to work the Web Browsers are going to have to track the
correspondence of name to roots in any case. So it basically becomes a
consistency thing. If it makes sense to do that centrally, it makes sense
for CABForum to be the venue. But it is an 'emergent' process.

On Fri, Jun 28, 2013 at 4:32 PM, Trevor Perrin <[email-masked]> wrote:

>> During the evolution of the draft we went through pretty much every
>> scheme mentioned in this thread. In the end we decided to go with a domain
>> name that is asserted for that purpose by the CA. So symantec.com /
>> comodo.com / etc.
>>
>
> Makes sense.
>
> HPKP still needs to map the declared domain name to a set of keys.
>  Perhaps CAs could maintain a list at a "well-known" URI derived from the
> domain name?
>

-- 
Website: http://hallambaker.com/