Herkese açık web sitenizi ESNI ile nasıl korursunuz?

Привет Хабр, меня зовут Илья, я работаю в платформенной команде компании Exness. Мы разрабатываем и внедряем базовые инфраструктурные компоненты, которые используют наши продуктовые команды разработки.

В этой статье я бы хотел поделиться опытом внедрения технологии encrypted SNI (ESNI) в инфраструктуре публичных веб-сайтов.

Herkese açık web sitenizi ESNI ile nasıl korursunuz?

Использование этой технологии позволит повысить уровень безопасности при работе с публичным веб-сайтом и соответствовать внутренним стандартам безопасности, принятым в Компании.

Прежде всего, хочу обратить внимание, что технология не стандартизована и все еще находится в драфте, однако CloudFlare и Mozilla уже поддерживают ее (в taslak01). Это и мотивировало нас на такой эксперимент.

Biraz teori

ESNI – это расширение к протоколу TLS 1.3, которое позволяет шифровать SNI в сообщении «Client Hello» TLS handshake.  Вот, как выглядит Client Hello с поддержкой ESNI (вместо привычного SNI мы видим ESNI):

Herkese açık web sitenizi ESNI ile nasıl korursunuz?

 Чтобы использовать ESNI, необходимы три составляющие:

  • DNS; 
  • Поддержка со стороны клиента;
  • Поддержка со стороны сервера.

DNS

Необходимо добавить две DNS записи – Ave TXT (TXT запись содержит публичный ключ, с помощью которого клиент может зашифровать SNI) – см. ниже. Кроме того, должна быть поддержка Sağlık Bakanlığı (DNS over HTTPS), так как доступные клиенты (см. ниже) не активируют поддержку ESNI без DoH. Это логично, так как ESNI подразумевает шифрацию имени ресурса, к которому мы обращаемся, то есть бессмысленно обращаться к DNS по UDP. Более того,  использование DNSSEC позволяет защититься от «cache poisoning» атак в этом сценарии.

На текущий момент доступно несколько DoH провайдеров, среди них:

CloudFlare заявляет (Check My Browser → Encrypted SNI → Learn More), что их серверы уже сейчас поддерживают ESNI, то есть для серверов CloudFlare в DNS мы имеем как минимум две записи – А и TXT. В примере ниже мы запрашиваем Google DNS (over HTTPS): 

А kayıt:

curl 'https://dns.google.com/resolve?name=www.cloudflare.com&type=A' 
-s -H 'accept: application/dns+json'
{
  "Status": 0,
  "TC": false,
  "RD": true,
  "RA": true,
  "AD": true,
  "CD": false,
  "Question": [
    {
      "name": "www.cloudflare.com.",
      "type": 1
    }
  ],
  "Answer": [
    {
      "name": "www.cloudflare.com.",
      "type": 1,
      "TTL": 257,
      "data": "104.17.210.9"
    },
    {
      "name": "www.cloudflare.com.",
      "type": 1,
      "TTL": 257,
      "data": "104.17.209.9"
    }
  ]
}

TXT запись, запрос формируется по шаблону _esni.FQDN:

curl 'https://dns.google.com/resolve?name=_esni.www.cloudflare.com&type=TXT' 
-s -H 'accept: application/dns+json'
{
  "Status": 0,
  "TC": false,
  "RD": true,
  "RA": true,
  "AD": true,
  "CD": false,
  "Question": [
    {
    "name": "_esni.www.cloudflare.com.",
    "type": 16
    }
  ],
  "Answer": [
    {
    "name": "_esni.www.cloudflare.com.",
    "type": 16,
    "TTL": 1799,
    "data": ""/wEUgUKlACQAHQAg9SiAYQ9aUseUZr47HYHvF5jkt3aZ5802eAMJPhRz1QgAAhMBAQQAAAAAXtUmAAAAAABe3Q8AAAA=""
    }
  ],
  "Comment": "Response from 2400:cb00:2049:1::a29f:209."
}

Итак, с точки зрения DNS, мы должны использовать DoH (желательно с DNSSEC) и добавить две записи. 

Поддержка со стороны клиента

Если мы говорим о браузерах, то на сегодняшний момент поддержка реализована только в FireFox. öyle приведена инструкция, как активировать поддержку ESNI и DoH в FireFox. После того, как браузер настроен, мы должны увидеть примерно такую картину:

Herkese açık web sitenizi ESNI ile nasıl korursunuz?

Bağlantı для проверки браузера.

Разумеется, для поддержки ESNI должен быть использован TLS 1.3, так как ESNI – это расширение к TLS 1.3.

Для целей тестирования бэкенда с поддержкой ESNI мы реализовали клиента на go, Ama daha sonra.

Поддержка со стороны сервера

На текущий момент ESNI не поддерживается web-серверами типа nginx/apache и т.д., так как они работают с TLS посредством OpenSSL/BoringSSL, в которых ESNI официально не поддерживается.

Поэтому мы решили создать свой front-end компонент (ESNI reverse proxy), который бы поддерживал терминацию TLS 1.3 с ESNI и проксирование HTTP(S) траффика на апстрим, не поддерживающий ESNI. Это позволяет применять технологию в уже сложившейся инфраструктуре, без изменения основных компонентов – то есть использовать текущие web-серверы, не поддерживающие ESNI. 

Для наглядности приведем схему:

Herkese açık web sitenizi ESNI ile nasıl korursunuz?

Отмечу, что прокси задумывался с возможностью терминировать TLS соединение без ESNI, для поддержки клиентов без ESNI. Также, протокол общения с апстримом может быть как HTTP, так и HTTPS c версией TLS ниже 1.3 (если апстрим не поддерживает 1.3). Такая схема дает максимальную гибкость.

Реализацию поддержки ESNI на go мы позаимствовали у CloudFlare. Сразу отмечу, что сама реализация достаточно нетривиальная, так как подразумевает изменения в стандартной библиотеке kripto/tl и поэтому требует «патчинга» MUHTEŞEM montajdan önce.

Для генерации ESNI ключей мы использовали esnitool (тоже детище CloudFlare). Данные ключи используются для шифрации/дешифрации SNI.
Мы протестировали сборку с использованием go 1.13 на Linux (Debian, Alpine) и MacOS. 

Пара слов об эксплуатационных особенностях

ESNI reverse proxy предоставляет метрики в формате Prometheus, например, такие, как rps, upstream latency & response codes, failed/successful TLS handshakes & TLS handshake duration. На первый взгляд это показалось достаточным для оценки того, как прокси справляется с трафиком. 

Также перед использованием мы провели нагрузочное тестирование. Результаты ниже:

wrk -t50 -c1000 -d360s 'https://esni-rev-proxy.npw:443' --timeout 15s
Running 6m test @ https://esni-rev-proxy.npw:443
  50 threads and 1000 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     1.77s     1.21s    7.20s    65.43%
    Req/Sec    13.78      8.84   140.00     83.70%
  206357 requests in 6.00m, 6.08GB read
Requests/sec:    573.07
Transfer/sec:     17.28MB 

Нагрузочное тестирование мы проводили чисто качественное, для сравнения схемы с использованием ESNI reverse proxy и без. Мы «наливали» трафик локально для того, чтобы исключить «помехи» в промежуточных компонентах.

Итак, с поддержкой ESNI и проксированием на апстрим с HTTP,  мы получили в районе ~ 550 rps с одного инстанса, при этом среднее потребление CPU/RAM ESNI reverse proxy:

  • 80% CPU Usage (4 vCPU, 4 GB RAM хосты, Linux)
  • 130 MB Mem RSS

Herkese açık web sitenizi ESNI ile nasıl korursunuz?

Для сравнения, RPS для того же апстрима nginx без терминации TLS (HTTP протокол) ~ 1100:

wrk -t50 -c1000 -d360s 'http://lb.npw:80' –-timeout 15s
Running 6m test @ http://lb.npw:80
  50 threads and 1000 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     1.11s     2.30s   15.00s    90.94%
    Req/Sec    23.25     13.55   282.00     79.25%
  393093 requests in 6.00m, 11.35GB read
  Socket errors: connect 0, read 0, write 0, timeout 9555
  Non-2xx or 3xx responses: 8111
Requests/sec:   1091.62
Transfer/sec:     32.27MB 

Наличие таймаутов говорит о том, что есть нехватка ресурсов (мы использовали 4 vCPU, 4 GB RAM хосты, Linux), и по факту потенциальный RPS выше (мы получали цифры до  2700 RPS на более мощных ресурсах).

В заключение отмечу, что технология ESNI выглядит достаточно перспективно. Есть еще много открытых вопросов, например, вопросы хранения публичного ESNI ключа в DNS и  ротирование ESNI-ключей – эти вопросы активно обсуждаются, а последняя версия драфта (на момент написания) ESNI уже 7.

Kaynak: habr.com

Yorum ekle