TLS
рукостискання, що шифрує сполучення
Що це таке
TLS, Transport Layer Security, — це протокол, який клієнт і сервер виконують перед обміном даними застосунку, щоб усе подальше лишалося приватним, не змінювалося непомітно й потрапляло саме до тієї сторони, до якої звертався клієнт. У меню продуктів і в магазинах сертифікатів його досі часто називають SSL, за назвою Secure Sockets Layer, протоколу, на зміну якому прийшов TLS. RFC 7568 у червні 2015 року визнав останню версію SSL застарілою, тож усе, що сьогодні зветься SSL, усередині є TLS або принаймні має ним бути.
TLS розташований між протоколом застосунку й транспортом під ним. HTTPS — це HTTP усередині TLS-з’єднання, а той самий шар захищає надсилання пошти, IMAP, з’єднання з базами даних і протокол Gemini. HTTP/3 іде далі: він працює поверх QUIC, що вбудовує рукостискання TLS 1.3 просто в транспорт, а не кладе його поверх TCP, як описує RFC 9001.
Чинна версія — TLS 1.3 з RFC 8446, опублікованого в серпні 2018 року, який замінив специфікацію TLS 1.2. З’єднання починається з рукостискання. Клієнт надсилає ClientHello з переліком версій, наборів шифрів (cipher suites) і ключових часток, які він підтримує, а також Server Name Indication (SNI) — ім’я хоста, до якого він звертається, щоб одна адреса могла обслуговувати багато сайтів. Сервер обирає, відповідає власною ключовою часткою, і з цієї миті все шифрується, зокрема й сертифікат, який він показує. Ланцюжок сертифікатів пов’язує ключ сервера з центром сертифікації, якому клієнт уже довіряє. Клієнт перевіряє ланцюжок, ім’я та дати, а сервер доводить, що володіє відповідним закритим ключем, підписуючи рукостискання.
Версію 1.3 від 1.2 відрізняють три речі. Повне рукостискання займає один обмін туди й назад замість двох. Кожен обмін ключами ефемерний, тож запис сьогоднішнього трафіку лишається закритим, навіть якщо закритий ключ сервера витече наступного року; ця властивість називається прямою секретністю (forward secrecy). І клієнт, що повертається, може надіслати «ранні дані» вже в першому повідомленні — це називають 0-RTT, — але RFC прямо називає ціну: такі дані не мають ні прямої секретності, ні захисту від повторного відтворення в іншому з’єднанні.
Чому це важливо
Чи використовувати TLS, давно вирішено. Відкритими лишаються два питання: які версії приймати і де закінчується зашифроване з’єднання.
Спершу версії. RFC 8996 від березня 2021 року забороняє використовувати TLS 1.0 і 1.1. RFC 9325, чинна найкраща практика (BCP 195), вимагає підтримки 1.2, рекомендує 1.3 і велить віддавати перевагу 1.3, коли його мають обидві сторони. Вона ж каже, що 1.2, налаштований за її вказівками, такий самий безпечний, як 1.3: обмін ключами ECDHE заради прямої секретності, AES-GCM і жодного набору з режимом CBC. Тож реальний вибір — між «1.2 і 1.3» та «лише 1.3». Сервіс лише з 1.3 має меншу поверхню атаки й коротшу конфігурацію, але відсікає кожного клієнта, що вміє щонайбільше 1.2: старіший телефон, платіжний термінал, інтеграцію партнера на старому середовищі Java. Таке рішення легко ухвалити на фактах. Кілька тижнів журналюйте узгоджену версію, а тоді вирішуйте за цифрами, а не за тим, що звучить сучасно.
Потім термінування. Там, де закінчується TLS, лежить закритий ключ, і трафік знову стає відкритим текстом. Є три поширені місця:
- CDN або хмарний балансувальник навантаження. Постачальник тримає ключ або сертифікат, який сам випустив, бере на себе навантаження рукостискань і бачить кожен запит відкритим текстом. Ділянці від нього до вихідного сервера потрібен власний TLS, інакше вона йде мережею незашифрованою.
- Зворотний проксі на хості, наприклад Nginx, Apache, HAProxy чи Caddy. Один процес тримає ключі всіх сайтів машини й передає запити без шифрування застосункам за ним через loopback або локальний сокет.
- Сам застосунок. Кожен сервіс має власний ключ. Це пасує трафіку між сервісами та взаємному TLS (mutual TLS), де сертифікат показує й клієнт, і водночас множить місця, де сертифікат треба поновлювати.
Більшість інфраструктур поєднує всі три. Важливо, щоб для кожної ділянки хтось міг сказати, де лежить відкритий текст і хто здатен його прочитати.
Як це виглядає на практиці
Власний робочий хост компанії показує, як мало потребує термінування TLS. Це хмарний примірник з одним ядром, 512 МБ пам’яті, з яких можна використати близько 464 МБ, і 10 ГБ диска. На ньому один вебсервер термінує TLS для всіх семи доменів поруч із хостингом git-репозиторіїв, onion-службою Tor, сервером Gemini, сервером Gopher, зашифрованими резервними копіями та блокуванням вторгнень. Це термінування на хості в найпростішому вигляді: один вебсервер відповідає за всі сім сайтів, і їхнє шифрування не вимагає ані окремої машини, ані окремого сервісу. Проєкт виходив із пам’яті як головного обмеження, і все з цього переліку, разом із термінуванням, вміщується в ті 464 МБ.
Правило, за яким працює компанія, записане на її сторінці безпеки: шифрування під час передавання всюди, а сертифікати випускаються й поновлюються без участі людини, і моніторинг стежить за кожним поновленням. Пояснення там таке: найпоширеніша причина збоїв TLS — сертифікат, строк якого спливає непомітно. Нижче зібрано збої, що лишаються, коли поновлення вже працює саме.
Де виникають проблеми
Частина клієнтів обривається з помилкою рукостискання, хоча браузери під’єднуються без проблем. Сторони не мають спільної версії чи набору шифрів. Хтось звузив сервер до 1.3 або до короткого списку наборів, і старішому клієнту нема чого запропонувати; або сервер, що досі пропонує лише набори CBC, натрапляє на клієнта, який від них відмовився. Повідомлення звучить як «handshake failure» чи «protocol version» і майже нічого не пояснює. Перевірте кожну версію ззовні:
openssl s_client -connect example.com:443 \
-servername example.com -tls1_2 </dev/null
Журналюйте узгоджені версію й набір шифрів, перш ніж щось звужувати, і тримайте 1.2 з ECDHE та AES-GCM поруч із 1.3, доки журнали не покажуть, що ним ніхто не користується.
Браузери довіряють сертифікату, а скрипти, curl і мобільні застосунки його
відхиляють. Сервер надсилає власний сертифікат без проміжного, що пов’язує
його з кореневим. Браузери часто заповнюють прогалину проміжними сертифікатами,
які вже бачили, або самі завантажують відсутній; більшість бібліотек TLS не
робить ні того, ні іншого й повідомляє про невідомого видавця. Налаштуйте повний
файл ланцюжка — спочатку сертифікат сервера, потім проміжні — і перевіряйте
клієнтом без кешу: рядок з openssl s_client вище показує ланцюжок, який сервер
справді надіслав.
Перевірку сертифікатів вимикають «тимчасово», і це налаштування потрапляє в
робоче середовище. Тестовий сервер із самопідписаним сертифікатом або
корпоративний проксі, що перепідписує трафік, — і хтось додає verify=False,
curl -k, InsecureSkipVerify чи NODE_TLS_REJECT_UNAUTHORIZED=0. З’єднання
лишається зашифрованим, але з будь-ким, хто відповість: будь-хто посередині може
показати власний сертифікат і прочитати все. Додайте потрібний сертифікат центру
сертифікації до сховища довіри клієнта або вкажіть його клієнту явно, а в CI
зробіть ці прапорці причиною падіння збірки.
Частина відвідувачів отримує сертифікат зовсім іншого сайту. Сервер показує типовий сертифікат, коли ClientHello називає хост, для якого в нього немає конфігурації, або взагалі не називає хоста. Так буває, коли новий домен спрямовують на адресу раніше, ніж з’явився його серверний блок, і коли зонд моніторингу чи старий клієнт не надсилає SNI. Браузер показує помилку імені, а на спільному хості заодно повідомляє відвідувачу, які ще сайти там живуть. Дайте кожному публічному імені власний блок і сертифікат, що його покриває, а типовий блок нехай відхиляє рукостискання замість показувати чужий сертифікат.
Платіж чи інший запит виконується двічі. Термінатор приймає ранні дані 0-RTT, усередині них приходить POST, і зловмисник або автоматичний повтор у мережі надсилає ті самі байти знову в новому з’єднанні. RFC 8446 дозволяє серверу приймати ранні дані й лишає захист від повторів між з’єднаннями застосунку. Приймайте ранні дані лише для запитів, які безпечно повторювати, або нехай термінатор відхиляє їх для всього іншого; RFC 8470 визначає для цього статус 425 Too Early.
Що почитати далі
- RFC 8446: The Transport Layer Security (TLS) Protocol Version 1.3 — рукостискання, рівень записів і додаток про те, чому дані 0-RTT можна відтворити повторно.
- RFC 9325: Recommendations for Secure Use of TLS and DTLS — BCP 195: які версії дозволяти, які набори шифрів для 1.2 залишати, і правила для SNI.
- RFC 8996: Deprecating TLS 1.0 and TLS 1.1 — формальне завершення двох старіших версій і його причини.
- RFC 8470: Using Early Data in HTTP — як термінатор і сервер домовляються, які запити можна надсилати в 0-RTT, і статус 425.