Session ticket TLS giúp kết nối lại nhanh hơn, nhưng ticket do máy chủ cấp và bạn gửi lại nguyên vẹn cũng là ID liên kết được, không cần cookie hay JavaScript.
Phần lớn các bài giải thích về theo dõi dừng lại ở tầng trình duyệt: cookie, localStorage, dấu vân tay tính bằng JavaScript. Nhưng bên dưới tất cả, HTTPS còn một tầng nữa, và nó tự lưu trạng thái. Sau một lần bắt tay TLS, máy chủ có thể đưa cho trình duyệt một khối dữ liệu nhỏ mà trình duyệt không đọc được — session ticket (vé phiên) — để trình duyệt lưu lại và gửi lại ở kết nối kế tiếp, nhờ đó bỏ qua phần lớn quá trình bắt tay. Nó tồn tại để web nhanh hơn. Nhưng nó cũng là một định danh do máy chủ cấp, bạn gửi lại y nguyên, và không nút xóa cookie nào chạm tới được.
Điểm chính
- Khôi phục phiên là một tính năng tăng tốc chính đáng. Trong TLS 1.3, máy chủ gửi thông điệp
NewSessionTicketsau khi bắt tay; client dùng nó ở kết nối sau để nối lại bằng khóa chia sẻ trước thay vì bắt tay đầy đủ. - Ticket vốn là một ID. Máy chủ tạo ra, client lưu mà không đọc được và gửi lại nguyên văn. Nếu máy chủ nhúng một định danh vào ticket hoặc đơn giản là ghi nhớ ticket nào đã cấp cho ai, nó sẽ nhận ra client quay lại — không cần JavaScript, cookie hay API lưu trữ.
- Thời hạn chính là cửa sổ theo dõi, và nó có thể nối chuỗi. RFC 8446 giới hạn thời hạn công bố của một ticket tối đa bảy ngày, nhưng máy chủ có thể cấp ticket mới ở mỗi lần nối lại, kéo dài liên kết từ lần truy cập này sang lần khác miễn là client còn quay lại trong cửa sổ.
- Ticket hiện rõ trên đường truyền. Nó nằm trong ClientHello, nên một bên quan sát thụ động trên đường đi có thể liên kết các kết nối dùng lại cùng một ticket. RFC 8446 yêu cầu client không dùng lại ticket cho nhiều kết nối chính vì lý do này.
- Trên thực tế nó không phải siêu cookie toàn cục. Các trình duyệt hiện đại giới hạn phạm vi cache phiên để bên thứ ba không thể nối lại phiên giữa các trang không liên quan, và hủy cache ở ranh giới duyệt riêng tư — nhưng hành vi cụ thể khác nhau theo trình duyệt và phiên bản, nên hãy kiểm chứng thay vì giả định.
Khôi phục phiên để làm gì
Một lần bắt tay TLS đầy đủ tốn thời gian: một vòng đi-về để thỏa thuận tham số, một chứng chỉ phải gửi và xác minh, một chữ ký phải tính. Với một trang mở cả chục kết nối, chi phí này cộng dồn lại đáng kể. Cơ chế khôi phục phiên cho phép client và máy chủ đã xác thực lẫn nhau bỏ qua phần tốn kém nhất.
Trong TLS 1.3, được định nghĩa ở RFC 8446, việc này hoạt động qua khóa chia sẻ trước (PSK). Sau khi bắt tay xong, máy chủ có thể gửi một hoặc nhiều thông điệp NewSessionTicket (mục 4.6.1). Mỗi thông điệp mang một ticket không đọc được, một thời hạn và dữ liệu để client suy ra PSK. Ở kết nối sau, client đưa ticket đó vào phần mở rộng pre_shared_key của ClientHello (mục 4.2.11), chứng minh mình giữ bí mật tương ứng bằng binder, và máy chủ có thể chấp nhận việc nối lại thay vì bắt tay đầy đủ với chứng chỉ.
Cùng cơ chế này cho phép 0-RTT early data (mục 2.3): client có thể gửi dữ liệu ứng dụng ngay trong lượt gửi đầu tiên. RFC nói thẳng rằng điều này có cái giá của nó, đáng chú ý là early data có thể bị phát lại (replay), và trình bày chi tiết ở mục 8. Với bài này, ý chính đơn giản hơn: khôi phục phiên là tính năng tốt, và ở đây không có cửa hậu nào. Vấn đề quyền riêng tư nằm ở chỗ ai nắm giữ trạng thái và trạng thái tồn tại bao lâu — chứ không phải ở lỗi mật mã học.
TLS 1.2 cũng có ý tưởng này dưới hai dạng — session ID và session ticket (RFC 5077) — nên những gì trình bày dưới đây không phải điều mới của TLS 1.3. Giới học thuật đã chỉ ra khả năng liên kết này từ nhiều năm trước; tài liệu thường được dẫn là bài báo Tracking Users across the Web via TLS Session Resumption của Sy, Burkert, Federrath và Fischer tại ACSAC 2018.
Ticket trở thành định danh như thế nào
Ticket là không đọc được đối với client. Trình duyệt không thể xem bên trong, chỉ lưu những gì máy chủ đưa. Chính sự "mờ đục" này khiến ticket trở thành viên gạch tiện dụng cho máy chủ — và cũng là một kênh theo dõi tiềm năng.
Hai thiết kế phổ biến:
- Ticket do máy chủ tự mã hóa. Máy chủ nhét trạng thái phiên vào ticket, mã hóa bằng khóa chỉ mình nắm giữ và không lưu gì. Bất kỳ máy chủ nào có khóa đều giải mã được.
- Tra cứu phía máy chủ. Ticket chỉ là một mã ngẫu nhiên; máy chủ lưu trạng thái trong bảng theo mã đó.
Dù theo cách nào, máy chủ kiểm soát nội dung ticket và thấy đúng các byte bạn gửi lại. Giao thức không ngăn máy chủ đặt một định danh riêng cho từng client vào đó, hay đơn giản là ghi log giá trị ticket nào quay lại. TLS 1.3 có làm rối tuổi của ticket (trường obfuscated_ticket_age), nhưng bản thân ticket vẫn được gửi nguyên vẹn. Kết quả là một ID ổn định, máy chủ nhận ra được, nằm ngay trong quá trình bắt tay TLS chứ không phải trong header HTTP hay kho lưu trữ mà script đọc được. Đây là cùng một kiểu với siêu cookie ETag, chỉ thấp hơn một tầng — và khác với ETag, JavaScript của trang không thể đọc, ghi hay xóa nó.
Vấn đề nối chuỗi
Giới hạn bảy ngày nghe như một rào cản tự nhiên. RFC 8446 quả thực quy định máy chủ không được dùng thời hạn ticket lớn hơn 604800 giây — bảy ngày. Nhưng giới hạn áp dụng cho một ticket, không phải cho danh tính đằng sau nó.
Sau mỗi lần nối lại thành công, máy chủ được tự do gửi một NewSessionTicket mới. Client thay ticket cũ bằng ticket mới, và lần truy cập sau sẽ trình ra ticket đó. Mỗi mắt xích làm mới cửa sổ, nên client kết nối lại ít nhất vài ngày một lần có thể bị theo dõi vô thời hạn, mỗi ticket mang cùng một danh tính đi tiếp. Thời hạn công bố giới hạn từng ticket, nhưng không giới hạn một máy chủ liên tục cấp lại.
Phần nối chuỗi này là điều mà hầu hết lời giải thích bỏ qua, và là lý do câu "ticket hết hạn sau một tuần" đánh giá thấp mức độ phơi nhiễm với một trang bạn vào hằng ngày. Dù vậy, trên thực tế trình duyệt thường tự đặt giới hạn ngắn hơn cho việc dùng lại một phiên đã lưu, và cache trong bộ nhớ sẽ mất khi trình duyệt được thoát hẳn — nên mức trần thực tế thường do client quyết định, chứ không phải con số bảy ngày trong RFC.
Vì sao trên thực tế nó không phải siêu cookie
Sẽ sai nếu kết luận rằng mọi trang bạn từng vào đều có thể theo dõi bạn qua ticket TLS. Rủi ro bị giới hạn bởi cách trình duyệt định phạm vi cache phiên:
- Ai được nối lại phiên nào. Một phiên được thiết lập khi bạn ở trang này không nên được cùng một máy chủ bên thứ ba, nhúng trên một trang khác không liên quan, nối lại. Phân vùng trạng thái mạng (kết nối, cache HTTP, phiên TLS) theo trang cấp cao nhất là cách tiếp cận chung, giống những gì trình duyệt đã làm với cache HTTP sau khi việc theo dõi qua cache trở nên phổ biến.
- Duyệt riêng tư và xóa dữ liệu. Cửa sổ riêng tư nên bắt đầu với cache phiên trống và hủy nó khi đóng, còn việc "xóa tất cả" hoặc khởi động lại trình duyệt thường đặt lại cache trong bộ nhớ.
Hãy xem đây là mô tả một loại biện pháp phòng thủ, không phải cam kết cho một bản phát hành cụ thể: cách mỗi trình duyệt phân vùng hoặc xóa cache phiên đã thay đổi theo thời gian, vì vậy chúng tôi cố ý không nêu số phiên bản. Điều còn lại là theo dõi trong cùng một trang: một trang (hoặc CDN phục vụ nó) vẫn nhận ra các kết nối quay lại của bạn, và bên quan sát mạng vẫn có thể liên kết các kết nối dùng lại rõ ràng cùng một ticket.
Phụ lục của chính RFC 8446 về chống theo dõi client (Appendix C.4) nêu ý đồ thiết kế: client không nên dùng lại ticket cho nhiều kết nối, vì việc dùng lại cho phép bên quan sát thụ động liên kết chúng. Trình duyệt làm theo lời khuyên này sẽ hạn chế những gì bên quan sát trên đường đi biết được, nhưng không làm gì với chính máy chủ đã cấp ticket.
Hiệu ứng thứ hai: khôi phục phiên làm đổi dấu vân tay
Có một tác dụng phụ liên quan trực tiếp đến loạt bài về tầng mạng của blog này. Một lần bắt tay được nối lại trông khác với lần bắt tay đầy đủ trên đường truyền. Nó có thêm phần mở rộng pre_shared_key (bắt buộc phải là phần mở rộng cuối cùng trong ClientHello) chứa ticket và binder, và có thể thêm phần mở rộng early_data. Vì thế danh sách phần mở rộng và tổng độ dài ClientHello đều khác so với kết nối đầu tiên.
Dấu vân tay TLS như JA3 hay JA4 được tính từ ClientHello, nên cùng một trình duyệt có thể cho giá trị khác ở kết nối được nối lại so với kết nối mới. Nếu bạn xây phát hiện dựa trên dấu vân tay TLS — hoặc đang tìm hiểu vì sao một client có hai mã băm — thì việc nối lại phiên là một nguồn biến thiên cần tính đến. Về cơ chế, xem TLS Fingerprinting và bộ JA4+; với tầng truyền tải mới hơn, HTTP/3 và QUIC Fingerprinting cũng có hiện tượng tương tự.
Vị trí trong các định danh bền vững
| Tầng | Định danh | Ai lưu | Xóa cookie có xóa được không? |
|---|---|---|---|
| Script | Visitor ID bền vững | JavaScript của trang, rải trên nhiều kho lưu trữ | Thường một phần |
| Cache HTTP | Siêu cookie ETag | Cache trình duyệt | Không |
| TLS | Session ticket | Cache phiên của TLS stack | Không chắc chắn |
Bài học không phải là tầng nào cũng là thảm họa — mỗi tầng đều có biện pháp giảm thiểu ở trình duyệt — mà là "tôi đã xóa cookie" chỉ mô tả hàng trên cùng của một chồng những nơi có thể chứa trạng thái.
Bạn thực sự làm được gì
Gần như không điều nào ở đây quan sát được từ bên trong trang, và đó chính là vấn đề. Những gì bạn có thể làm:
- Đừng trông vào việc xóa cookie để đặt lại cache phiên TLS. "Xóa cookie" có xóa luôn các phiên TLS đã lưu hay không là tùy trình duyệt; thoát hẳn rồi mở lại trình duyệt, hoặc "xóa tất cả", là cách đáng tin cậy hơn.
- Cửa sổ riêng tư là ranh giới sạch nhất, vì chúng được thiết kế để bắt đầu và kết thúc mà không mang theo trạng thái phiên.
- Đừng mong có công cụ hiển thị ticket của bạn. BrowserInsight không kiểm tra ticket TLS của bạn và không báo cáo trạng thái khôi phục phiên. Thứ nó hiển thị là những gì máy chủ quan sát được từ lần bắt tay TLS của bạn — phiên bản giao thức, bộ mã hóa, độ dài ClientHello và mã băm phần mở rộng — trong thẻ mạng của công cụ kiểm tra dấu vân tay; cùng tín hiệu đó cũng được dùng trong công cụ phát hiện bot. Lưu ý rằng các giá trị này mô tả kết nối đã mang yêu cầu, và bản thân kết nối đó có thể là một kết nối được khôi phục.


