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

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

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

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

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

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

Малко теория

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

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

 За да използвате ESNI, имате нужда от три компонента:

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

DNS

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

Достъпен в момента няколко доставчици на DoH, между тях:

CloudFlare състояния (Проверете My Browser → Encrypted SNI → Learn More), че техните сървъри вече поддържат ESNI, тоест за сървърите на CloudFlare в DNS имаме поне два записа - A и TXT. В примера по-долу правим запитване към Google DNS (през 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

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

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

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

Поддръжка от страна на сървъра

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

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

За по-голяма яснота ето диаграма:

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

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

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

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

Няколко думи за оперативните характеристики

Обратният прокси ESNI предоставя показатели във формат Prometheus, като rps, латентност нагоре и кодове за отговор, неуспешни/успешни TLS ръкостискания и продължителност на TLS ръкостискане. На пръв поглед това изглежда достатъчно, за да се оцени как проксито обработва трафика. 

Извършихме и тестове за натоварване преди употреба. Резултати по-долу:

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 обратен прокси и без. Ние "изляхме" трафика локално, за да елиминираме "намесата" в междинните компоненти.

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

  • 80% използване на процесора (4 виртуални процесора, 4 GB RAM хостове, Linux)
  • 130 MB Mem RSS

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

За сравнение, 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.

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

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