Bagaimana melindungi situs publik Anda dengan ESNI

Halo Habr, nama saya Ilya, saya bekerja di tim platform di Exness. Kami mengembangkan dan menerapkan komponen infrastruktur inti yang digunakan tim pengembangan produk kami.

Pada artikel kali ini saya ingin berbagi pengalaman saya dalam menerapkan teknologi SNI terenkripsi (ESNI) pada infrastruktur website publik.

Bagaimana melindungi situs publik Anda dengan ESNI

Penggunaan teknologi ini akan meningkatkan tingkat keamanan saat bekerja dengan situs publik dan memenuhi standar keamanan internal yang dianut oleh Perusahaan.

Pertama-tama, saya ingin menunjukkan bahwa teknologinya belum terstandarisasi dan masih dalam rancangan, tetapi CloudFlare dan Mozilla sudah mendukungnya (dalam draf01). Hal ini memotivasi kami untuk melakukan eksperimen semacam itu.

Sedikit teori

ESNI adalah ekstensi dari protokol TLS 1.3 yang memungkinkan enkripsi SNI dalam pesan jabat tangan TLS "Client Hello". Inilah tampilan Client Hello dengan dukungan ESNI (bukan SNI biasa yang kita lihat ESNI):

Bagaimana melindungi situs publik Anda dengan ESNI

 Untuk menggunakan ESNI, Anda memerlukan tiga komponen:

  • DNS; 
  • Dukungan klien;
  • Dukungan sisi server.

DNS

Anda perlu menambahkan dua catatan DNS – ADan TXT (Catatan TXT berisi kunci publik yang dapat digunakan klien untuk mengenkripsi SNI) - lihat di bawah. Selain itu, harus ada dukungan DoH (DNS melalui HTTPS) karena klien yang tersedia (lihat di bawah) tidak mengaktifkan dukungan ESNI tanpa DoH. Ini logis, karena ESNI menyiratkan enkripsi nama sumber daya yang kita akses, sehingga tidak masuk akal untuk mengakses DNS melalui UDP. Apalagi kegunaannya DNSSEC memungkinkan Anda melindungi terhadap serangan peracunan cache dalam skenario ini.

Tersedia saat ini beberapa penyedia DoH, diantara mereka:

CloudFlare menyatakan (Periksa Browser Saya β†’ SNI Terenkripsi β†’ Pelajari Lebih Lanjut) bahwa server mereka sudah mendukung ESNI, yaitu untuk server CloudFlare di DNS kami memiliki setidaknya dua catatan - A dan TXT. Pada contoh di bawah ini kami menanyakan Google DNS (melalui HTTPS): 

А pintu masuk:

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 catatan, permintaan dibuat sesuai dengan templat _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."
}

Jadi, dari perspektif DNS, kita harus menggunakan DoH (sebaiknya dengan DNSSEC) dan menambahkan dua entri. 

Dukungan pelanggan

Jika kita berbicara tentang browser, maka saat ini dukungan hanya diterapkan di FireFox. Di sini Berikut petunjuk cara mengaktifkan dukungan ESNI dan DoH di FireFox. Setelah browser dikonfigurasi, kita akan melihat sesuatu seperti ini:

Bagaimana melindungi situs publik Anda dengan ESNI

Link untuk memeriksa peramban.

Tentu saja TLS 1.3 harus digunakan untuk mendukung ESNI, karena ESNI merupakan perpanjangan dari TLS 1.3.

Untuk tujuan pengujian backend dengan dukungan ESNI, kami mengimplementasikan klien go, Tapi lebih lanjut tentang itu nanti.

Dukungan sisi server

Saat ini, ESNI tidak didukung oleh server web seperti nginx/apache, dll., karena mereka bekerja dengan TLS melalui OpenSSL/BoringSSL, yang tidak secara resmi mendukung ESNI.

Oleh karena itu, kami memutuskan untuk membuat komponen front-end kami sendiri (proksi terbalik ESNI), yang akan mendukung penghentian TLS 1.3 dengan ESNI dan proksi lalu lintas HTTP(S) ke hulu, yang tidak mendukung ESNI. Hal ini memungkinkan teknologi untuk digunakan pada infrastruktur yang sudah ada, tanpa mengubah komponen utama - yaitu menggunakan server web saat ini yang tidak mendukung ESNI. 

Agar lebih jelas, berikut diagramnya:

Bagaimana melindungi situs publik Anda dengan ESNI

Saya perhatikan bahwa proxy tersebut dirancang dengan kemampuan untuk mengakhiri koneksi TLS tanpa ESNI, untuk mendukung klien tanpa ESNI. Selain itu, protokol komunikasi dengan upstream dapat berupa HTTP atau HTTPS dengan versi TLS lebih rendah dari 1.3 (jika upstream tidak mendukung 1.3). Skema ini memberikan fleksibilitas maksimal.

Implementasi dukungan ESNI pada go kami meminjam dari CloudFlare. Saya ingin segera mencatat bahwa implementasinya sendiri tidak sepele, karena melibatkan perubahan pada perpustakaan standar kripto/tls dan oleh karena itu memerlukan β€œpenambalan” GOROOT sebelum perakitan.

Untuk menghasilkan kunci ESNI kami menggunakan esnitool (juga merupakan gagasan CloudFlare). Kunci ini digunakan untuk enkripsi/dekripsi SNI.
Kami menguji build menggunakan go 1.13 di Linux (Debian, Alpine) dan MacOS. 

Beberapa kata tentang fitur operasional

Proksi terbalik ESNI menyediakan metrik dalam format Prometheus, seperti rps, latensi upstream & kode respons, jabat tangan TLS yang gagal/berhasil & durasi jabat tangan TLS. Pada pandangan pertama, ini tampaknya cukup untuk mengevaluasi bagaimana proxy menangani lalu lintas. 

Kami juga melakukan pengujian beban sebelum digunakan. Hasil di bawah ini:

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 

Kami melakukan pengujian beban kualitatif murni untuk membandingkan skema yang menggunakan proksi terbalik ESNI dan tanpa proksi terbalik. Kami "menuangkan" lalu lintas secara lokal untuk menghilangkan "interferensi" pada komponen perantara.

Jadi, dengan dukungan ESNI dan proksi ke upstream dari HTTP, kami mendapatkan sekitar ~550 rps dari satu instance, dengan rata-rata konsumsi CPU/RAM dari proksi terbalik ESNI:

  • Penggunaan CPU 80% (4 vCPU, host RAM 4 GB, Linux)
  • 130 MB Anggota RSS

Bagaimana melindungi situs publik Anda dengan ESNI

Sebagai perbandingan, RPS untuk nginx upstream yang sama tanpa penghentian TLS (protokol HTTP) adalah ~ 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 

Adanya batas waktu menunjukkan kurangnya sumber daya (kami menggunakan 4 vCPU, host RAM 4 GB, Linux), dan faktanya potensi RPS lebih tinggi (kami menerima angka hingga 2700 RPS pada sumber daya yang lebih kuat).

Sebagai kesimpulan, saya perhatikan bahwa teknologi ESNI terlihat cukup menjanjikan. Masih banyak pertanyaan terbuka, misalnya masalah penyimpanan kunci ESNI publik di DNS dan rotasi kunci ESNI - masalah ini sedang aktif dibahas, dan draf ESNI versi terbaru (pada saat penulisan) sudah ada. 7.

Sumber: www.habr.com

Tambah komentar