Subject: Difffent ways to authenticate (Was: Re: Report on preliminary decision on TLS 1.3 and client auth)

From: Ilari Liusvaara <[email-masked]>
Date: Thu, 1 Oct 2015 17:57:52 +0300

On Wed, Sep 23, 2015 at 08:41:24PM -0700, Martin Thomson wrote:
> On 23 September 2015 at 19:02, Amos Jeffries <[email-masked]> wrote:
> >
> > Option 2 risks the same mess if the AUTH frame is defined end-to-end.
> > But a per-hop frame would work nicely as long as it is clear to server
> > implementers that intermediaries may be the source of the certificate.
> > Not some "user".
> 
> This would naturally be hop-by-hop, by virtue of extensions being
> hop-by-hop and by virtue of the setting that enables it also being
> hop-by-hop.

Thinking about problem space of this certificate authentication in
HTTP/2 (long, given that this is about just about every way I can
come up with, including ones I don't think work):

(The terminology choices are probably pretty horrible).

On high level, one needs mechanisms for two things:
1) Client to authenticate that it can represent given authority
   (anonymous authority can always be represented, also "authority"
   here has nothing to do with :authority pseudo-header).
2) Client to associate authority it can represent (does not guarantee
   the server will authorize the request!) with every request (HTTP
   is stateless by nature!).

There are essentially two ways to do the latter:

a) Select authority used by connection request is routed to/from
b) Select authority used by in-band indication.

If selecting 1), select authority by connection, the obvious choice
for authenticating the authority is TLS client certs. There remains
choice on how to handle the case where authority with no existing
connection exists:

c) Change authority of existing connection (open new connection if
   old authority is needed again).
d) Open a new connection with desired authority, possibly keeping
   the old one.

The problem with c) is that the existing connection can have active
transfers and can't change authority until those transfers either
end or are aborted (to do otherwise could very well create races
that are exploitable for privilege escalation). d) Creates more
connections, but the number should remain fairly limited (much
less than HTTP/1.1 in the same situation).

I think d) is clearer of those two (it is essentially what
CANT+CARE(?) proposal was).

If selecting 2), select authority by in-band indication, the TLS
client certs don't work, because one needs to authenticate multiple
authorities on one connection, and there can be only one TLS
client cert.

Thus the certificate sending and verification needs to be implemented
at HTTP/2 level. The main choices are:

e) PUT/POST special resource
f) New HTTP verb
g) New HTTP/2 frame type.

Now, this kind of authentication is inherently hop-by-hop and could
target destination other than origin, so e) doesn't actually work
properly. HTTP verbs could be useful if one wanted to also make the
mechanism also work for HTTP/1.1, but as we see when considering
request authority designation, this type of mechanism doesn't work
properly in HTTP/1.1. This leaves new HTTP/2 frame type.

The choices for how the in-band indication for request authority
is done are:

h) New pseudo-header
i) New header
j) New HEADERS flag + field
k) New HTTP/2 frame that changes authority newly opened streams
   get.
l) SETTINGS that sets the authority for newly opened streams.

h) Doesn't work: HTTP/1.1 doesn't have pseudo-headers and HTTP/2
forbids defining new ones. i) Doesn't work either, since the mechanism
is hop-by-hop by nature and HTTP/2 does not have per-hop headers (other
than one or two special cases).

This leaves j), k) and l), which both seem workable (but HTTP/2-
specfic). In k), the frame could theoretically be combined with the
frame that shows the client can represent the authority, but in
practice this might be a bad idea due to bandwidth usage (certificates
might not be exactly small and changes might not be rare).

k) and l) need to be synchronous against stream opens (SETTINGS is
already synchronous against everything it can appear in middle of)
so to avoid race conditions resulting server misinterpretting
client request.

For reference, the SPDY client auth was equivalent to g) and j)
(frame type for showing client can represent authority and header
frame field for authority designation).

For in-band operation, I think either g)+j) or g)+l) is the
cleanest (the remaining one seems quite odd to me), with maybe a
slight preference for g)+j).

As note: Originally I had h)-k), but then noticed that the authority
designation fits in SETTINGS value (even 32 bits is overkill) so I
added l) to the list.

Finally, some considerations on adding HTTP-level certificate
verification mechanism:
- Only signature certificates work, but fortunately non-signature
  certificates are close to non-existent.
- Use channel bindings to bind to lower layer. Make sure those
  bindings are actually bindings (i.e. nonces, e.g. TLS THS attack)!
- The data to be signed should include the lower layer name, so if
  the thing runs on top of multiple lower layer protocols (HTTPS
  definition says "secure transport" not "TLS"!) one doesn't get
  cross-protocol attacks.
- Sign the certificate too, some signature schemes don't even
  bind the key properly and nothing binds certificate.
- One probably wants to keep support for crap like (non-EC) DSA or
  SHA-1 (or worse, MD5!) out entierely.
- Copying TLS 1.3 client signature format (but with different
  context) might be a good idea.

> Also, while I think of it, we should probably forbid the use of this
> on server-initiated streams (i.e., with server push).  That could
> cause problems.

I think reasonable way to handle authority of server pushed streams
would be for those to inherit authority of the associated stream.

-Ilari

---

From: Martin Thomson <[email-masked]>
Date: Mon, 19 Oct 2015 15:10:17 -0700

On 23 September 2015 at 10:16, Martin Thomson <[email-masked]> wrote:
> Here is a summary of the applicable pieces, plus what I options it provides
> HTTP/2...

With the help of Mike Bishop [7], I've just submitted a draft that
describes option 2 in more detail, including something for TLS 1.2.

  https://tools.ietf.org/html/draft-thomson-http2-client-certs-00

I think that this is the best of all the bad options available to us.
In an ideal world, I think that I would prefer to kill this feature,
but we tried that once already and it wasn't working so well.  So we
this is plan B.

The TLS 1.2 option requires a new TLS extension.  If we think that
this is a good idea, we'll have to coordinate with the TLS working
group.

--Martin

[7] Mike is on vacation, and I did make a few changes without his
approval, so I'll have to ask forgiveness if I made a mistake...  In
other words, all the blame is mine, and the credit Mike's.

---

From: Ilari Liusvaara <[email-masked]>
Date: Tue, 20 Oct 2015 09:24:56 +0300

>   https://tools.ietf.org/html/draft-thomson-http2-client-certs-00

How does client refuse to change authentication on existing connection
and open a new one for new authentication[1]?

Because client can be rather easily forced into situation where the
existing connection can't change authentication without resetting
potentially numerious streams first (e.g. streams from cross-origin
XMLHttpRequest/Fetch non-credentials[2][3]).

Or is the browser supposed to reset all offending streams before
changing authentication?

[1] AFAIK, Chrome does it like that even with HTTP/1.1.

[2] Change authentication with such stream open and you have an
security hole.

[3] And there are more cases relating to coalescing where things
go wrong (altough _probably_ not in exploitable way) if one tries to
change auth.

-Ilari

---

From: "Jason T. Greene" <[email-masked]>
Date: Tue, 20 Oct 2015 08:31:05 -0400 (EDT)

> On Oct 20, 2015, at 1:33 AM, Ilari Liusvaara <[email-masked]> wrote:
> 

Wouldn't the semantics be a hell of a lot cleaner, and implementations a lot simpler, if we just pushed this to an HTTP cert auth protocol? 

Having two different TLS solutions which in and of themselves are brittle sounds like a recipe for security vulnerabilities and broken interop.

---

From: Martin Thomson <[email-masked]>
Date: Tue, 20 Oct 2015 11:08:21 -0700

> and open a new one for new authentication[1]?

A client can always ignore attempts to renegotiate, or it can offer an
empty certificate in response to a CertificateRequest.  I think the
latter is cleaner.

Keep in mind that the client has signalled a willingness to
participate in this protocol.

> XMLHttpRequest/Fetch non-credentials[2][3]).

I'm sorry, I couldn't parse this statement.

> Or is the browser supposed to reset all offending streams before

What would make a particular stream offensive?

---

From: Martin Thomson <[email-masked]>
Date: Tue, 20 Oct 2015 11:10:31 -0700

> Wouldn't the semantics be a hell of a lot cleaner, and implementations a lot simpler, if we just pushed this to an HTTP cert auth protocol?

Yes, yes it would.  A better authentication mechanism might be better
still.  But that would be a new protocol.  We have plenty of evidence
to suggest that a new protocol would not be acceptable.  As I said, we
are already at plan B.

---

From: Kyle Rose <[email-masked]>
Date: Thu, 22 Oct 2015 14:11:20 -0400

Sorry to take so long to respond to this, but life and work get in the way.
I wanted to give a perspective on how one enterprise uses client
certificates in real life, and the reasons for that.

Like all great decisions, the one my company made to introduce client certs
was the result of a compromise. The original requirement was for a second
factor ("something you have") in addition to a password ("something you
know"), because the thing you know is often of poor quality in order to
make remembering it easy: the standard way to get around password re-use
restrictions on rotation, for instance, is to take a prefix that never
changes and append a number in monotonically-increasing fashion at each
rotation. (Why password rotation in the first place? Because PCI DSS
§8.2.4.)

Trials were performed with HOTP tokens, which did not go well for various
reasons I'm not privy to, though I've heard that will be revisited in the
next year or two.

There was also a desire to have a solution that reduced the attack surface.
This is where the decision to use client certs intersects with the
conversation here. One of the properties of our specific MO for client
certs is that a client cannot complete a TLS handshake with any of the RPs
without first presenting an acceptable client certificate (which may then
result in a redirect to the SSO IdP/token issuer if successful). What this
does is create a firewall around applications with potentially faulty
authorization logic. It also makes for a very poor user experience when
certificates expire, but this was deemed "acceptable" in compromise-logic.

Having a client cert also raises the bar ever so slightly to access from
unauthorized equipment. Yes, it's trivial to transfer the cert to a browser
on another machine, but this is a triviality that few will bother with,
especially among the demographic whose collective attention paid to
security is most troublesome. Keeping 3 year-old Android phones 2 years
past their most recent security update away from corporate resources is a
feature, not a bug.

Sealing the deal, our CDN happens to support client certificates: we can
eat our own dog food *and* do it while requiring a second factor. Profit.

The way in which this is relevant is that it would be nice to present the
user a better error than "ssl_error_handshake_failure_alert" in the case of
an expired or missing certificate. To that end, switching to a model in
which any client can connect and perform some actions, and only later be
required to authenticate, would be helpful. The downside of supporting this
is that the handshake-time firewall between RP authentication logic and
clients would be eliminated; the upside is that the first principle of IT
is bureaucratic inertia, so I suspect that justification would be silently
dropped in favor of keeping the second factor scheme and improving the UX.

As for H/1.1 and TLS <=1.2, we presumably disabled renegotiation entirely
due to the potential for downgrade attacks, and so are unlikely to solve
the UX issue prior to TLS 1.3.

Kyle

On Wed, Sep 23, 2015 at 1:16 PM, Martin Thomson <[email-masked]>
wrote:

> The minutes of the TLS interim have been posted. Some decisions regard
> client authentication were made.
>
>
> https://www.ietf.org/proceedings/interim/2015/09/21/tls/minutes/minutes-interim-2015-tls-3
>
> Here is a summary of the applicable pieces, plus what I options it
> provides HTTP/2...
>
> (Caveat here: aspects of this could change if new information is
> presented, but it seems unlikely that there will be changes that will
> affect the core decisions.)
> The big change is that a server can request client authentication at any
> time. A server may also make multiple such requests. Those multiple
> requests could even be concurrent.
>
> The security claims associated with client authentication require more
> analysis before we can be certain, but the basic idea is that
> authentication merely provides the proof that a server needs to regard the
> entire session to be authentic. In other words, client authentication will
> apply retroactively. This could allow a request sent prior to
> authentication to be considered authenticated. This is a property that is
> implicitly relied on for the existing renegotiation cases and one that we
> might want to exploit.
>
> Each certificate request includes an identifier that allows it to be
> correlated with the certificate that is produced in response. This also
> allows for correlating with application context. This is what I think that
> we can use to fix HTTP/2.
>
> Clients cannot spontaneously authenticate, which invalidates the designs I
> have proposed, however, the basic structure is the basis for the first
> option that I will suggest.
>
>
> Option 1 uses a new authentication scheme. A request that causes a server
> to require a client certificate is responded to with a 4xx response
> containing a ClientCertificate challenge. That challenge includes an
> identifier.  The server also sends - at the TLS layer - a
> CertificateRequest containing the same identifier, allowing the client to
> correlate it's HTTP request with the server's CertificateRequest.
>
> Client@HTTP/2:
> HEADERS
>   :method = GET ...
>
> Server@HTTP/2:
> HEADERS
>   :status = 401
>   authorization = ClientCertificate req="option 1"
>
> Server@TLS:
> CertificateRequest { id: "option 1" }
>
> Client@TLS:
> Certificate+CertificateVerify { id: "option 1", certificates... }
>
> Client@HTTP/2:
> HEADERS
>   :method = GET ...
>
> Server@HTTP/2:
> HEADERS
>   :status = 200
>
>
> Option 2 aims to more closely replicate the experience we get from
> renegotiation in HTTP/1.1 + TLS <= 1.2.  Rather than rejecting the request,
> the server sends an HTTP/2 frame on the stream to indicate to the client to
> expect a CertificateRequest. That frame includes the identifier.
>
> Client@HTTP/2:
> HEADERS
>   :method = GET ...
>
> Server@HTTP/2:
> EXPECT_AUTH
>   id = option 2
>
> Server@TLS:
> CertificateRequest { id: "option 2" }
>
> Client@TLS:
> Certificate+CertificateVerify { id: "option 2", certificates... }
>
> Server@HTTP/2:
> HEADERS
>   :status = 200
>
> In this case, the server probably wants to know that the client is willing
> to respond to these requests, otherwise it will want to use
> HTTP_1_1_REQUIRED or 421.  So a companion setting to enable this is a good
> idea (the semantics of the setting that Microsoft use for renegotiation is
> pretty much exactly what we'd need).
>
> I think that the first option has some architectural advantages, but that
> is all.  The latter more closely replicates what people do today and for
> that reason, I think that it is the best option.
>
>
> As for how to implement this same basic mechanism in TLS 1.2, I have an
> idea that will work for either option, but it's a bit disgusting, so I'll
> save that for a follow-up email.
>

---

From: Martin Thomson <[email-masked]>
Date: Thu, 22 Oct 2015 14:03:27 -0700

On 22 October 2015 at 11:11, Kyle Rose <[email-masked]> wrote:
> an expired or missing certificate.

That is a choice the server makes.  The server is perfectly able to
complete a handshake and then deny the HTTP request.  I know that many
do not because that's more work to do right, but it's an option.

I wouldn't interpret this as a defense of the client certificate UX in
browsers.  But I don't expect that to change significantly, our UX
people have a lot of work to do, most of it much more important than
this.

---

From: Kyle Rose <[email-masked]>
Date: Thu, 22 Oct 2015 19:43:28 -0400

> people have a lot of work to do, most of it much more important than

I wasn't even actually talking about the browser UI (though I guess I would
like Firefox to actually "remember this decision" for client certificates,
which it doesn't seem to do even when I check that box). I'm more talking
about the UX suggested by your first paragraph, in which the server accepts
the handshake and provides a better error. Given the solutions proposed to
the client authentication problem, I suspect that's what we'll end up
doing, bugs in application authorization logic be damned. Using client
certs as a firewall for permission-to-talk does seem like a hack: having a
simple TCB up to the point of client authentication seems like a better
solution all around.

Tl;dr: don't interpret my previous email as a defense of that use case.

Kyle

---

From: Yoav Nir <[email-masked]>
Date: Mon, 26 Oct 2015 09:03:08 +0200

> On 20 Oct 2015, at 9:10 PM, Martin Thomson <[email-masked]> wrote:
> 

An HTTP cert auth protocol is just an HTTP authentication method, much like Basic, Digest or the experimental ones we’re standardizing in http-auth. The framework is already there in all clients and servers.  It has the advantage that you don’t have to skip between protocol layers - it’s all in HTTP. This way the client is in control of which streams are authenticated and which are not, so Imari’s security hole could go away. 

Practically you probably don’t want to sign each request, so applications are likely to set a cookie (or tokbind) after a single authentication and use that to continue the authentication to other resources, but that’s the way they do it with other forms of authentication anyway.

Yoav

---

From: Mike Bishop <[email-masked]>
Date: Mon, 26 Oct 2015 18:00:37 +0000

Yes, the new thing would be "just" a new authentication mechanism, but the existing apps call down to the HTTP layer requesting the client cert, which asks TLS to either return or obtain the client cert.

As Martin has said, there's no opposition to a cert-based, or key-based, HTTP auth protocol being defined.  That's definitely something beneficial to the Internet that people could move toward in the future.  However, in the short term, we have applications which rely on the existing APIs and mechanisms -- the goal with this draft is simply as a compatibility measure.  Currently, those applications cannot use HTTP/2 over TLS 1.2 without one of the various hacks; in TLS 1.3, it will be less hacky.

You're arguing for a more idealized solution -- that's something I can get behind as a long-term direction, but we also have short-term issues that need a solution.  One does not preclude the other.

-----Original Message-----
From: Yoav Nir [mailto:[email-masked]] 
Sent: Monday, October 26, 2015 12:03 AM
To: Martin Thomson <[email-masked]>
Cc: Jason T. Greene <[email-masked]>; Ilari Liusvaara <[email-masked]>; HTTP Working Group <[email-masked]>
Subject: Re: Report on preliminary decision on TLS 1.3 and client auth

Yoav

---

From: Yoav Nir <[email-masked]>
Date: Wed, 28 Oct 2015 13:35:30 +0200

I’m not sure I follow. As you say, existing apps call the HTTP layer requesting the client cert. The HTTP layer does some magic, which could be a TLS 1.2 renegotiation, a TLS 1.3 Certificate Request, or a new HTTP authentication. Either way, the HTTP layer finally returns to the application with either a certificate or a lack thereof.

Both ways, the app does not need to change. That’s a good thing. Why would we need to change the APIs?

Yoav

> On 26 Oct 2015, at 8:00 PM, Mike Bishop <[email-masked]> wrote:
>