Как да защитите публичния си сайт с ESNI

Здравейте, Хабър, аз съм Иля, работя в платформения екип на компанията Exness. Разработваме и внедряваме основни инфраструктурни компоненти, които използват нашите екипи за разработка на продукти.

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

Как да защитите публичния си сайт с ESNI

Използването на тази технология ще повиши нивото на сигурност при работа с публичен уебсайт и ще отговаря на вътрешните стандарти за сигурност, приети в компанията.

Първо, искам да обърна внимание, че технологията не е стандартизирана и все още е в проект, но CloudFlare и Mozilla вече я поддържат (в draft01). Това е и мотивацията ни за подобен експеримент.

Няколко теории

ESNI е разширение на протокола TLS 1.3, което позволява шифроване на SNI в съобщението „Client Hello“ по време на TLS handshake. Ето как изглежда Client Hello с поддръжка на ESNI (вместо обичайния SNI виждаме ESNI):

Как да защитите публичния си сайт с ESNI

 За да се използва ESNI, са необходими три компонента:

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

DNS

Необходимо е да добавите две DNS записа – A, и TXT (TXT записа съдържа публичния ключ, с който клиентът може да шифрова SNI) – вижте по-долу. Освен това, трябва да има поддръжка DoH (DNS over HTTPS), тъй като наличните клиенти (вижте по-долу) не активират поддръжката на ESNI без DoH. Това е логично, тъй като ESNI предвижда шифроване на името на ресурса, до който се обръщаме, следователно е безсмислено да се обръщаме към DNS по UDP. Освен това, използването на DNSSEC позволява защита от атаки за „cache poisoning“ в този сценарий.

В момента са налични няколко предоставячи на DoH, сред които:

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

А запис:

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.. Тук Представяме инструкции как да активирате подкрепата за ESNI и DoH в FireFox. След като браузърът е настроен, трябва да видим нещо подобно:

Как да защитите публичния си сайт с ESNI

Линк за проверка на браузъра.

Разбира се, за подкрепа на ESNI трябва да се използва TLS 1.3, тъй като ESNI е разширение на TLS 1.3.

За целите на тестване на бэкента с подкрепа на ESNI разработихме клиент на go, но за това малко по-късно.

Подкрепа от страна на сървера

В момента ESNI не се поддържа от уеб сървъри като nginx/apache и т.н., тъй като те работят с TLS през OpenSSL/BoringSSL, в които ESNI официално не се поддържа.

Затова решихме да създадем собствен front-end компонент (ESNI reverse proxy), който да поддържа терминирането на TLS 1.3 с ESNI и проксирането на HTTP(S) трафика към upstream, който не поддържа ESNI. Това позволява технологиятa да се използва в съществуваща инфраструктура, без да се променят основните компоненти – тоест да се използват текущи уеб сървъри, които не поддържат ESNI. 

За по-ясно представяне, ето схемата:

Как да защитите публичния си сайт с ESNI

Обръщам внимание, че проксито е замислено с възможността да терминира TLS връзка без ESNI, за да поддържа клиенти без ESNI. Също така, протоколът за комуникация с upstream може да бъде както HTTP, така и HTTPS с версия на TLS под 1.3 (ако upstream не поддържа 1.3). Тази схема дава максимална гъвкавост.

Реализацията на подкрепата за ESNI на go познахме от CloudFlare. Веднага отбелязвам, че самата реализация е достатъчно нетривиална, тъй като предвижда промени в стандартната библиотека crypto/tls и затова изисква "пачинг" GOROOT преди компилация.

За генериране на ESNI ключове използвахме esnitool (също творение на CloudFlare). Тези ключове се използват за шифроване/дешифриране на SNI.
Тествахме компилацията с използване на go 1.13 на Linux (Debian, Alpine) и MacOS. 

Няколко думи за експлоатационните особености.

ESNI обратный прокси предоставя метрики в формате Prometheus, например, като rps, upstream latency & response codes, неуспешни/успешни TLS handshake-ове & продължителност на TLS handshake. На пръв поглед това изглеждаше достатъчно, за да оценим как проксито се справя с трафика. 

Също така, преди използването на решението проведохме натоварвателно тестване. Резултатите са по-долу:

wrk -t50 -c1000 -d360s 'https://esni-rev-proxy.npw:443' --timeout 15s
Изпълнение на 6-минутно тестване @ https://esni-rev-proxy.npw:443
  50 нишки и 1000 връзки
  Статистика за нишките  Avg      Stdev     Max    +/- Stdev
    Latency     1.77s     1.21s    7.20s    65.43%
    Req/Sec    13.78      8.84   140.00     83.70%
  206357 заявки в 6.00m, 6.08GB прочетени
Заявки/сек:    573.07
Прехвърляне/сек:     17.28MB 

Натоварвателното тестване, което проведохме, беше чисто качествено, за да сравним схемата с използването на ESNI обратен прокси и без него. Ние „наляхме“ трафик локално, за да изключим „смутовете“ в междинните компоненти.

Така че, с поддръжка на ESNI и проксиране към upstream с HTTP, получихме около ~ 550 rps от един инстанс, при това средното потребление на CPU/RAM на ESNI обратния прокси:

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

Как да защитите публичния си сайт с ESNI

За сравнение, RPS за същия upstream nginx без терминиране на TLS (HTTP протокол) ~ 1100:

wrk -t50 -c1000 -d360s 'http://lb.npw:80' --timeout 15s
Изпълнение на 6-минутно тестване @ http://lb.npw:80
  50 нишки и 1000 връзки
  Статистика за нишките  Avg      Stdev     Max    +/- Stdev
    Latency     1.11s     2.30s   15.00s    90.94%
    Req/Sec    23.25     13.55   282.00     79.25%
  393093 заявки в 6.00m, 11.35GB прочетени
  Грешки в сокетите: connect 0, read 0, write 0, timeout 9555
  Не-2xx или 3xx отговори: 8111
Заявки/сек:   1091.62
Прехвърляне/сек:     32.27MB 

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

В заключение, бих хотел да подчертая, че технологията ESNI изглежда доста обещаваща. Има още много отворени въпроси, например, въпросите за съхранение на публичния ESNI ключ в DNS и ротация на ESNI ключовете – тези въпроси се обсъждат активно, а последната версия на черновата (към момента на писане) на ESNI вече 7.

Източник: habr.com

Купете надежден хостинг за сайтове със защита от DDoS, VPS и VDS сървъри 🔥 Купете надежден хостинг за сайтове със защита от DDoS, VPS и VDS сървъри | ProHoster