Subject: [Acme] tls-sni-01 validation compromise

From: Jehiah Czebotar <[email-masked]>
Date: Thu, 21 Jan 2016 21:38:24 -0500

In working to implemented LetsEncrypt at Bitly, I uncovered an issue
with the tls-sni-01 validation that limits its trustworthiness in
validation.

Issue:

The tls-sni-01 validation is intended to prove control over a domain
name. The challenge relies on presenting a
"<Zi[0:32]>.<Zi[32:64]>.acme.invalid" as the ServerName in the
clientHello, and relying on a self-signed certificate to used to
complete the TLS handshake that has a single matching
subjectAlternativeName/DNSName.

Because the server initiating the validation request is presenting the
full ServerName expected back, it is thus untrusted and can not be
used to imply any relation to the party requesting validation. It is
possible to configure a server that generates certificates on-the-fly
to match the ServerName presented, and thus passing ALL tls-sni-01
validation attempts. An example of such a server is
https://gist.github.com/jehiah/a5b508b8f4efad08e67a

Suggestions:

1) Change the requirement that the self signed cert have one DNSName,
and require the response to have TWO DNS names. One that matches the
requested hostname, and a second that is secret which proves it can
only be created by the appropriate party initiating validation
2) Remove reliance on SNI matching, and make the challenge `tls-01`
and fulfill the same HTTP response requirements as `http-01` where the
Hostname, and request path are untrusted, but the response body with
full keyAuthorization proves the connection to the requestor. This
opens up the possibility of TLS validation against the $domain being
validated instead of relying on a .acme.invalid hostname.

Other Notes:

There appear to have been discussions about `n` versions of
certificates being generated and requiring validation to check a
subset of them. I think this vulnerability means `n` is moot as no
number of checks for certificates that match the server hello prove
control over a domain name.

Thoughts?

p.s. Thank you to Richard Barnes from Mozilla for pointing me to this
mailing list.

-- 
Jehiah
http://jehiah.cz/

---

From: Martin Thomson <[email-masked]>
Date: Fri, 22 Jan 2016 14:03:01 +1100

> validated instead of relying on a .acme.invalid hostname.

I think that the suggestion that the challenge response include
something unique to the challenge (as http-01 already does) is a fine
suggestion.  I don't think that it matters much how that is done.  If
the intent is to verify that the requester exercises control over the
TLS server, having this restricted to things that are part of the TLS
server configuration is probably advisable.

To that end, adding a key authorization to the certificate would seem
to be the best option.  Whether that is done as a second
subjectAltName or as a separate extension probably doesn't matter
much.

Following through with a challenge like http-01 would work, but it
means playing with the configuration of the server in two places.

---

From: Martin Thomson <[email-masked]>
Date: Fri, 22 Jan 2016 15:09:25 +1100

Thinking more about this, if the intent is to make the tls-sni-xx
challenge analogous to the http-xx one, then the request would encode
the token (not a hash of the key authorization) and the response to
that challenge would include the key authorization (as I suggest
below).

---

From: Jehiah Czebotar <[email-masked]>
Date: Thu, 21 Jan 2016 23:31:22 -0500

Yes, I agree it's odd that the validation request is based on the key
authorization not the token. That's really the crux of the issue and
more succinct than my original message.

In option 1) I proposed, to be clear it would make sense to update
tls-sni-01 validation to use Z(token) as the SNI hostname, and use two
entries in the DNSName cert response. One matching the SNI hostname
based on Z(token) so the right cert is chosen, and one based on Z(key
authorization) that serves as the secret.

As you mentioned a new `tls-01` could depend on just the
request/response like `http-01` and not depend on details of the cert
so that you don't have to mess with configurations of both cert &
request handling as much.

--
Jehiah

---

From: Peter Eckersley <[email-masked]>
Date: Fri, 22 Jan 2016 11:49:37 -0800

> https://gist.github.com/jehiah/a5b508b8f4efad08e67a

This is certainly possible, and a reason to spec tls-sni-02 to fix the
issue.  For purposes of scheduling deprecatio of tls-sni-01, do we know
of any deployed implementations that have done things that way?  Making
the cert in response to the SNI request is usually going to be a bit
more involved, TLS-library-wise, than making it when the socket starts
listening. If nobody has deployed mid-handshake cert generation, we can
be a bit more gradual with the tls-sni-01 deprecation schedule.

-- 
Peter Eckersley                            [email-masked]
Chief Computer Scientist          Tel  +1 415 436 9333 x131
Electronic Frontier Foundation    Fax  +1 415 436 9993

---

From: Thomas Lußnig <[email-masked]>
Date: Fri, 22 Jan 2016 21:29:59 +0100

Hi,

these problem is not new. I raised the roblem with letsencrypt over an 
month ago.
Better Would be:
Client Send clientnone,ECDSA-SHA256(servernonce,clientnonce)

The cert for <fqdn>.challenge.amce contain the public key.
So the client had proved he control the server