xref: /curl/docs/internals/TLS-SESSIONS.md (revision fa0ccd9f)
1<!--
2Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
3
4SPDX-License-Identifier: curl
5-->
6
7# TLS Sessions and Tickets
8
9The TLS protocol offers methods of "resuming" a previous "session". A
10TLS "session" is a negotiated security context across a connection
11(which may be via TCP or UDP or other transports.)
12
13By "resuming", the TLS protocol means that the security context from
14before can be fully or partially resurrected when the TLS client presents
15the proper crypto stuff to the server. This saves on the amount of
16TLS packets that need to be sent back and forth, reducing amount
17of data and even latency. In the case of QUIC, resumption may send
18application data without having seen any reply from the server, hence
19this is named 0-RTT data.
20
21The exact mechanism of session tickets in TLSv1.2 (and earlier) and
22TLSv1.3 differs. TLSv1.2 tickets have several weaknesses (that can
23be exploited by attackers) which TLSv1.3 then fixed. See
24[Session Tickets in the real world](https://words.filippo.io/we-need-to-talk-about-session-tickets/)
25for an insight into this topic.
26
27These difference between TLS protocol versions are reflected in curl's
28handling of session tickets. More below.
29
30## Curl's `ssl_peer_key`
31
32In order to find a ticket from a previous TLS session, curl
33needs a name for TLS sessions that uniquely identifies the peer
34it talks to.
35
36This name has to reflect also the various TLS parameters that can
37be configured in curl for a connection. We do not want to use
38a ticket from an different configuration. Example: when setting
39the maximum TLS version to 1.2, we do not want to reuse a ticket
40we got from a TLSv1.3 session, although we are talking to the
41same host.
42
43Internally, we call this name a `ssl_peer_key`. It is a printable
44string that carries hostname and port and any non-default TLS
45parameters involved in the connection.
46
47Examples:
48- `curl.se:443:CA-/etc/ssl/cert.pem:IMPL-GnuTLS/3.8.7` is a peer key for
49   a connection to `curl.se:443` using `/etc/ssl/cert.pem` as CA
50   trust anchors and GnuTLS/3.8.7 as TLS backend.
51- `curl.se:443:TLSVER-6-6:CA-/etc/ssl/cert.pem:IMPL-GnuTLS/3.8.7` is the
52   same as the previous, except it is configured to use TLSv1.2 as
53   min and max versions.
54
55Different configurations produce different keys which is just what
56curl needs when handling SSL session tickets.
57
58One important thing: peer keys do not contain confidential
59information. If you configure a client certificate or SRP authentication
60with username/password, these will not be part of the peer key.
61
62However, peer keys carry the hostnames you use curl for. The *do*
63leak the privacy of your communication. We recommend to *not* persist
64peer keys for this reason.
65
66**Caveat**: The key may contain file names or paths. It does not
67reflect the *contents* in the filesystem. If you change `/etc/ssl/cert.pem`
68and reuse a previous ticket, curl might trust a server which no
69longer has a root certificate in the file.
70
71
72## Session Cache Access
73
74#### Lookups
75
76When a new connection is being established, each SSL connection filter creates
77its own peer_key and calls into the cache. The cache then looks for a ticket
78with exactly this peer_key. Peer keys between proxy SSL filters and SSL
79filters talking through a tunnel will differ, as they talk to different
80peers.
81
82If the connection filter wants to use a client certificate or SRP
83authentication, the cache will check those as well. If the cache peer
84carries client cert or SRP auth, the connection filter must have
85those with the same values (and vice versa).
86
87On a match, the connection filter gets the session ticket and feeds that
88to the TLS implementation which, on accepting it, will try to resume it
89for a shorter handshake. In addition, the filter gets the ALPN used
90before and the amount of 0-RTT data that the server announced to be
91willing to accept. The filter can then decide if it wants to attempt
920-RTT or not. (The ALPN is needed to know if the server speaks the
93protocol you want to send in 0-RTT. It makes no sense to send HTTP/2
94requests to a server that only knows HTTP/1.1.)
95
96#### Updates
97
98When a new TLS session ticket is received by a filter, it adds it to the
99cache using its peer_key and SSL configuration. The cache looks for
100a matching entry and, should it find one, adds the ticket for this
101peer.
102
103### Put, Take and Return
104
105when a filter accesses the session cache, it *takes*
106a ticket from the cache, meaning a returned ticket is removed. The filter
107then configures its TLS backend and *returns* the ticket to the cache.
108
109The cache needs to treat tickets from TLSv1.2 and 1.3 differently.
1101.2 tickets should be reused, but 1.3 tickets SHOULD NOT (RFC 8446).
111The session cache will simply drop 1.3 tickets when they are returned
112after use, but keep a 1.2 ticket.
113
114When a ticket is *put* into the cache, there is also a difference. There
115can be several 1.3 tickets at the same time, but only a single 1.2 ticket.
116TLSv1.2 tickets replace any other. 1.3 tickets accumulate up to a max
117amount.
118
119By having a "put/take/return" we reflect the 1.3 use case nicely. Two
120concurrent connections will not reuse the same ticket.
121
122## Session Ticket Persistence
123
124#### Privacy and Security
125
126As mentioned above, ssl peer keys are not intended for storage in a
127file system. They'll clearly show which hosts the user talked to. This
128maybe "just" privacy relevant, but has security implications as an
129attacker might find worthy targets among your peer keys.
130
131Also, we do not recommend to persist TLSv1.2 tickets.
132
133### Salted Hashes
134
135The TLS session cache offers an alternative to storing peer keys:
136it provides a salted SHA256 hash of the peer key for import and export.
137
138#### Export
139
140The salt is generated randomly for each peer key on export. The
141SHA256 makes sure that the peer key cannot be reversed and that
142a slightly different key still produces a very different result.
143
144This means an attacker cannot just "grep" a session file for a
145particular entry, e.g. if they want to know if you accessed a
146specific host. They *can* however compute the SHA256 hashes for
147all salts in the file and find a specific entry. But they *cannot*
148find a hostname they do not know. They'd have to brute force by
149guessing.
150
151#### Import
152
153When session tickets are imported from a file, curl only gets the
154salted hashes. The tickets imported will belong to an *unknown*
155peer key.
156
157When a connection filter tries to *take* a session ticket, it will
158pass its peer key. This peer key will initially not match any
159tickets in the cache. The cache then checks all entries with
160unknown peer keys if the passed key matches their salted hash. If
161it does, the peer key is recovered and remembered at the cache
162entry.
163
164This is a performance penalty in the order of "unknown" peer keys
165which will diminish over time when keys are rediscovered. Note that
166this also works for putting a new ticket into the cache: when no
167present entry matches, a new one with peer key is created. This
168peer key will then no longer bear the cost of hash computes.
169