
TLS 1.2 Session Tickets - gtank
https://blog.filippo.io/we-need-to-talk-about-session-tickets/
======
vbernat
The article and the comments here lack a bit of context. At the time where
tickets were starting to be deployed (around 2011), ECDH ciphers were not in
use and PFS was therefore quite expensive (and therefore, almost never used).
Around the same time, the move from 1024-bit certificates to 2048-bit
certificates were making TLS difficult to deploy at scale for everyone except
Google.

Session tickets were primarily a mechanism to help to reduce to load of TLS
connections. The reduced latency is a happy side-effect. At the time, it was
already well-known that this would negatively impact PFS (something I already
mention in my own blog article about session tickets in 2011).

Moreover, many seem to build a scenario where session tickets are recovered by
an attacker but not the private keys. Usually, session tickets are in memory
only and private keys are persisted. This makes those tickets more unlikely to
be recovered by anyone unless you get access to a running web server. In this
case, you have far more serious trouble than those tickets (even if the
attacker still cannot decode past recorded data).

Even today, TLS handshakes are quite expensive, both for the server and the
clients. Using session tickets (with rotated keys) still outweigh the
downsides of not using them. That's why every major site are using them.

------
xenophonf
So if like me you want to know how to disable session tickets in Apache httpd,
you need to be running at least httpd 2.4.8 with OpenSSL 1.0.2. Then you can
set:

    
    
      SSLOpenSSLConfCmd Options -SessionTicket
    

Alternatively, if you're running at least httpd 2.4.11 with OpenSSL 0.9.8f,
you can set this instead:

    
    
      SSLSessionTickets off
    

See also:

[https://httpd.apache.org/docs/2.4/mod/mod_ssl.html#sslopenss...](https://httpd.apache.org/docs/2.4/mod/mod_ssl.html#sslopensslconfcmd)

[https://httpd.apache.org/docs/2.4/mod/mod_ssl.html#sslsessio...](https://httpd.apache.org/docs/2.4/mod/mod_ssl.html#sslsessiontickets)

~~~
xenophonf
Also, if you're running RHEL/CentOS 7, you'll want to switch to the SCL
version of Apache httpd (2.4.25) as the version in the base repositories
(2.4.6) is too old to support either setting:

[https://www.softwarecollections.org/en/scls/rhscl/httpd24/](https://www.softwarecollections.org/en/scls/rhscl/httpd24/)

~~~
smegel
Well, technically RHEL7 runs httpd-2.4.6-67, which is RedHat's 67th patched
version of 2.4.6.

They backport a lot of stuff from later versions, don't see why they could not
have done this (they haven't, I checked).

------
tonyztan
> An attacker with the STEK doesn't need to wait until session resumption is
> attempted. Session Tickets containing the current session keys are sent at
> the beginning of every connection that merely supports Session Tickets. In
> plaintext on the wire, ready to be decrypted with the STEK, fully bypassing
> Diffie-Hellman.

I found this flaw to be the scariest, by far. It means that a connection with
"forward secrecy" gives up this property as soon as it begins.

~~~
zaroth
Gives up the property _before_ it begins even. But I completely agree with
you. I would go as far to say a connection does not have PFS if you are
sending the DH session key encrypted only by a [geographically replicated]
static key before the session starts. You are literally destroying the entire
purpose of the DH handshake in doing so.

I always assumed the STEK-encrypted session key was sent inside the
established TLS PFS stream. To send it outside the stream is mind-numbingly
insane!

Browsers should not report connections as having PFS if tickets are enabled in
TLS 1.2. This is like NSA slide "PFS added and removed here :-)"

~~~
tptacek
You're clearly not destroying _the entire purpose_ of the DH handshake,
because the STEK is rotated and so the time window of compromise is limited to
by the rotation interval. Even with poorly rotated ticket keys, you're still
better off with a PFS suite than without it --- without the DH, every client
has an unlimited window of exposure if the server's private key is
compromised.

~~~
zaroth
True, it does not destroy the entire purpose _if and when_ the STEK is
securely and properly rotated. But to call this PFS stretches the truth in
ways that boring crypto never does.

The number of real-world cases where PFS is reduced exactly to the non-PFS
state by the existence of session tickets with fixed STEKs is not only non-
zero, but rather I would venture a guess it is staggeringly large.

E.g. Configuring Session Tickets [1]

 _While Apache offers the SSLSessionTicketKeyFile directive to specify a key
file that should contain 48 random bytes, it is recommended to not specify one
at all. Apache will simply generate a random key on startup and use that to
encrypt session tickets for as long as it is running.

The good thing about this is that the session ticket key will not touch
persistent storage, the bad thing is that it will never be rotated. Generated
once on startup it is only discarded when Apache restarts. For most of the
servers out there that means they use the same key for months, if not years.

To provide forward secrecy we need to rotate the session ticket key about
daily and current Apache versions provide no way of doing that. The only way
to achieve that might be use a cron job to gracefully restart Apache daily to
ensure a new key is generated. That does not sound like a real solution though
and nothing ensures the old key is properly overridden._

...

 _Nginx, too, provides no way to automatically rotate keys. Reloading its
configuration daily using a cron job might work but does not come close to a
real solution either._

...

 _HAproxy does not allow configuring session ticket parameters. It implicitly
supports this feature because OpenSSL enables it by default. HAproxy will thus
always generate a session ticket key on startup and use it to encrypt tickets
for the whole lifetime of the process._

[1] - [https://timtaubert.de/blog/2014/11/the-sad-state-of-
server-s...](https://timtaubert.de/blog/2014/11/the-sad-state-of-server-side-
tls-session-resumption-implementations/)

Note that's from 2014 -- has it changed since then?

~~~
tptacek
The 'P' in 'PFS' has always stretched the truth, which is why a lot of
practitioners elide it.

Even if you never rotate STEKs and never reset systems to get a new STEK
accidentally, the DH handshake is still providing forward secrecy value for
clients who don't do session tickets.

~~~
zaroth
Do such clients actually exist in mainstream usage? The one crypto bug which
you are actually _protected_ from by using IE on Win7!

~~~
tptacek
I don't know, but the cost that session tickets are saving are almost entirely
an externality to clients, and the privacy benefit of not honoring tickets
isn't, so there would seem to be some value to a Chrome extension that busted
that cache.

~~~
zaroth
Yeah but it's not the client sending the resumption ticket but sending the
capability in the ClientHello which exposes the session key under a possibly
fixed key. So you can't fix this by busting the client cache I think. You need
the capability flag off.

Even so could an active adversary turn on the capability in the ClientHello
and still get a chance to see the STEK encrypted session key even against
legacy (or intentionally ticket-disabled) clients? This I don't know - would a
client just drop the ticket message and proceed or would it abort? But at
least it would have to be an active attacker.

~~~
tptacek
Yep, this got pointed out to me several times on Slack. Session tickets suck.

------
js2
If you're using OpenSSL, you can disable RFC 5077 Session Tickets via
SSL_OP_NO_TICKET:

[https://wiki.openssl.org/index.php/SSL/TLS_Client#Session_Ti...](https://wiki.openssl.org/index.php/SSL/TLS_Client#Session_Tickets)

Figuring out how to access that option from your library/language is an
exercise for the reader.

Looking into this for Python. Python 3 supports sessions and the tickets may
be disabled:

[https://docs.python.org/3/library/ssl.html#ssl.SSLSession](https://docs.python.org/3/library/ssl.html#ssl.SSLSession)

[https://docs.python.org/3/library/ssl.html#ssl.OP_NO_TICKET](https://docs.python.org/3/library/ssl.html#ssl.OP_NO_TICKET)

For Python 2, I see no way to get at the session object (at least on the
client side), much less disable session tickets:

[https://docs.python.org/2/library/ssl.html](https://docs.python.org/2/library/ssl.html)

Under Python 2, pyOpenSSL has better support. You can make use of it on
Python2 (and 3) via requests by installing `requests[security]` instead of
just `requests`. Using `[security]` causes requests to pull in pyOpenSSL,
cryptography and idna packages.

Under the covers, requests is using urllib3 and it ends up makes this call if
pyOpenSSL is installed:

    
    
        urllib3.contrib.pyopenssl.inject_into_urllib3()
    

[http://urllib3.readthedocs.io/en/latest/reference/urllib3.co...](http://urllib3.readthedocs.io/en/latest/reference/urllib3.contrib.html#module-
urllib3.contrib.pyopenssl)

That's as far as I've gotten. There's no documentation so it's going to
require reading the urllib3 source to figure out what's going on under the
hood.

Edit: nope, urllib3 doesn't support SSL session re-use. There's an open PR:

[https://github.com/shazow/urllib3/issues/590](https://github.com/shazow/urllib3/issues/590)

------
jgrahamc
Related:
[https://crypto.dance/projects/6239406](https://crypto.dance/projects/6239406)

~~~
sdevlin
What is Cloud Flare's policy for managing STEKs? Are they distinct per
geographic region as the article recommends?

~~~
prdonahue
TL/DR: we rotate them every hour, but need to keep history of previous STEKs
for 18 hours to support the maximum session lifetime:

$ openssl s_client -connect cloudflare.com:443 2>/dev/null | grep "lifetime
hint" TLS session ticket lifetime hint: 64800 (seconds)

\--

We've written about how we manage TLS session tickets here:
[https://blog.cloudflare.com/tls-session-resumption-full-
spee...](https://blog.cloudflare.com/tls-session-resumption-full-speed-and-
secure/).

Additionally, I wrote here about a bug that we encountered with Microsoft's
implementation of TLS session resumption:
[https://blog.cloudflare.com/microsoft-tls-downgrade-
schannel...](https://blog.cloudflare.com/microsoft-tls-downgrade-schannel-
bug/).

Here's a snippet from my blog post:

Session Tickets at CloudFlare CloudFlare’s solution to this problem,
documented in previous blog posts, is to frequently regenerate and synchronize
these session ticket keys across our entire global network. We currently do
this once per hour. This means we need a mechanism for turning over session
ticket keys. For instance, if a client instantiates an HTTPS session at
12:00pm and continues using that ticket past 1:00pm, our edge network will re-
encrypt the ticket with a brand new session ticket key.

To accomplish this, our web servers must have both the full history of all
previous keys that could have encrypted the ticket (i.e., one per hour dating
back to the maximum session lifetime of 64,800 seconds) as well as immediate
access to each newly generated key. The previous keys are used exclusively to
decrypt tickets presented by the client, while the new keys are used to
"refresh" the encryption on existing tickets and encrypt tickets for entirely
new sessions.

~~~
sdevlin
These are great details, but they don't really answer my question.

------
dward
One of the original goals of Google's QUIC[0] protocol was to achieve 0-RTT
handshake. I wonder how TLS 1.{2,3} Session Tickets will affect the future of
that protocol. Has it achieved it's purpose (applying the right pressure to
the TLS working group)? Will it be phased out now?

[0] [https://blog.chromium.org/2013/06/experimenting-with-
quic.ht...](https://blog.chromium.org/2013/06/experimenting-with-quic.html)

~~~
baby
I felt like Google was not going to move to TLS 1.3 if they had not included
0-RTT. Now that TLS 1.3 has agreed to include it, it seems logical to
terminate QUIC crypto.

------
e12e
Hm, looks like maybe a ticket should be filed against:

[https://github.com/ssllabs/research/wiki/SSL-and-TLS-
Deploym...](https://github.com/ssllabs/research/wiki/SSL-and-TLS-Deployment-
Best-Practices)

As it stands, the point on resumption reads a little on the positive side?:

> 3.2 Use Session Resumption

> Session resumption is a performance-optimization technique that makes it
> possible to save the results of costly cryptographic operations and to reuse
> them for a period of time. A disabled or nonfunctional session resumption
> mechanism may introduce a significant performance penalty.

Does http2 have similar issues with session resumption (especially:
compromised pfs)?

I came across this, which documents how cloudscape does this securely - at
least they rotate - but looks like read access to memcache+compromise of a
single web server is enough to compromise the past hour or two of ssl traffic
that goes through cloudflare?

[https://blog.cloudflare.com/tls-session-resumption-full-
spee...](https://blog.cloudflare.com/tls-session-resumption-full-speed-and-
secure/)

[ed: also came over this:

[https://github.com/mozilla/cipherscan](https://github.com/mozilla/cipherscan)

Which looks very handy for sanity-checking servers.]

~~~
Hello71
> I came across this, which documents how cloudscape does this securely - at
> least they rotate - but looks like read access to memcache+compromise of a
> single web server is enough to compromise the past hour or two of ssl
> traffic that goes through cloudflare?

but (depending on how you get in) you can probably also compromise the _next_
few hours, so one extra hour doesn't seem like a huge difference, given that
the scenario is somewhat unlikely in the first place.

~~~
e12e
It's relevant in terms of pfs. Say, you happen to have ciphertext of all
traffic to a domain for the past three years (whitepower.forum.example.ru),
and you get an urgent need to read that ciphertext. Now, if you could get
physical access to any one cloudflare server, and perhaps dump the ram, or do
a cold boot attack - that might have been enough to read all that data. Assume
for the sake of argument, that the servers hosting the site is (physically)
out of reach.

~~~
Hello71
aiui getting the current keys won't magically let you decrypt those three
years of traffic. that's the whole point of rotation.

~~~
e12e
Yes, I wasn't trying to say rotation is useless - just highlight that n
servers still lead to n avenues of getting at traffic for all n servers, and
that cloudflare did something to deal with the pfs issue. It's worse than n
servers without session resumption, but better than it could be.

[ed: per
[https://news.ycombinator.com/item?id=15360922](https://news.ycombinator.com/item?id=15360922)
the window is 18 hours]

------
CaliforniaKarl
If I understand the article correctly, are all of the issues listed addressed
by TLS 1.3?

~~~
colmmacc
TLS 1.3 still has problems with STEKs. If you use STEKs with 0-RTT mode, then
you lose forward secrecy and that's where the most sensitive data is likely to
be: your request, password, credit card number, etc ...

0-RTT doesn't have to use STEKs, there's a better way to do it, but TLS1.3
won't enforce or require it (though it could), so it'll be up to the
marketplace of ideas and security standards to sort it out.

~~~
niftich
0-RTT is an awful idea anyway; the equivalent of spray-and-pray. Some folks
under HTTPbis are working on an Internet Draft on Early Data's (~0-RTT's)
ramifications [1] in HTTP.

0-RTT trades performance at the expense of security properties inside the same
tunable protocol, which is the sort of wishy-washy stuff I (and others) were
hopeful we'd get away from, the same way PFS ciphersuites went from obscure to
preferred overnight, the same way cleartext HTTP has been marginalized, the
same way broken ciphersuites were aggressively blacklisted and underused
ciphersuites were pruned.

[1] [https://tools.ietf.org/html/draft-ietf-httpbis-
replay-00](https://tools.ietf.org/html/draft-ietf-httpbis-replay-00)

------
Bhilai
I understand that TLS 1.3 is in draft stage currently. Is there an expected
date of when TLS1.3 is supposed to be available in common libs and browsers ?

Also at current rate it sounds like it’s going to take years to phase out TLS
1.1. Would mordern browsers take a stand and refuse to initiate connections
for older versions of TLS and its not just browsers right, there are other odd
ball clients and enterprises using IE7 or something super old. I liked the way
Apple had taken a stand with App Transport Security initially but even they
backed down and pushed the deadline indefinitely.

~~~
Panino
> Also at current rate it sounds like it’s going to take years to phase out
> TLS 1.1.

It's already more or less phased out. On my webservers, TLS 1.1 accounts for
0.1% of traffic, and about half of that is junk requests like exploit
attempts.

Check out SSL Pulse, specifically the Protocol Support graph:

[https://www.ssllabs.com/ssl-pulse/](https://www.ssllabs.com/ssl-pulse/)

This used to show 100% support for TLS 1.0, which is now at 92.6% as some
sites are now going 1.2-only. That's just webserver _support_ , not _usage_.
Huge real-world difference. Like my car supports not wearing a seal-belt but I
always use it. The vast majority of usage is 1.2, and a large percentage of
1.0/1.1 traffic is unwanted garbage traffic. Hence why some people are
disabling 1.0/1.1 in their webservers. It also exposes more code for
questionable benefit.

BTW I'd love to know what sites support TLS 1.0 but not 1.2. What's the
breakdown of the Alexa Top 1000 or so? I suspect it's mostly banks and unknown
sites.

~~~
cesarb
> It's already more or less phased out.

Was TLS 1.1 ever "phased in"? It was the "most recent" TLS version for only a
couple of years, so the early adopters went quickly to TLS 1.2, while the late
adopters stayed at TLS 1.0 (or even "TLS 1.0 but disabled by default,
therefore actually SSL 3.0"). Once the later adopters catch up, there's no
reason for them to not jump directly to TLS 1.2.

------
ramshanker
We are doing all these session tickets gymnastic to reduce the round trip.
Mostly considering web applications. In case of native mobile application, we
could just add a splash-screen/quick 2 sec animation hiding the worst case
3x200ms round trip latency overhead. So at the start of the app we simply
create a fresh perfectly secured TLS connection.

It appears a better choice from security standpoint, UI can always be cleverly
tricked into being smooth.

~~~
gruturo
In case of 2.5G connections (I still get 2.5G on a regular basis, e.g. in some
specific train stations on my way to work in the morning) it can easily be
800ms per RTT - so 2.4 seconds before you have a chance to request any
content.

And no, you cannot really ignore this if you are interested in fostering TLS'
adoption in the real world. Every tenth of a second causes a measurable loss
in user interest, and in many organizations this metric will drive the
decision. We cannot legislate from our ivory tower and expect the world to
follow against their (perceived) best interest.

------
maerek
For HAProxy 1.7 (may be present in older versions, didn't check), there's the
'no-ssl-reuse'[0] directive for the server.

[0]:[https://cbonte.github.io/haproxy-
dconv/1.7/configuration.htm...](https://cbonte.github.io/haproxy-
dconv/1.7/configuration.html#5.2-no-ssl-reuse)

------
anonacct37
Well, on the bright side h2 and keepalive are other ways of reducing the
overall number of tls handhakes required if you're doing http.

Too bad session resumption has issues.

------
bearhsiung
NewSessionTicket is sent before ChangeCipherSpec means the message is not
encrypted using the master secret exchanged with the handshake and is not
necessary means that the session ticket is in plaintext. Quite contrary, it
has been encrypted using the server secret key. In the page 3 of RFC5077, it
states "a ticket that is encrypted and integrity-protected by a key known only
to the server." and in the page 11, "Tickets must be authenticated and
encrypted to prevent modification or eavesdropping by an attacker."

