วิธีปกป้องเว็บไซต์สาธารณะของคุณด้วย ESNI

สวัสดี Habr ฉันชื่อ Ilya ฉันทำงานในทีมแพลตฟอร์มที่ Exness เราพัฒนาและใช้งานส่วนประกอบโครงสร้างพื้นฐานหลักที่ทีมพัฒนาผลิตภัณฑ์ของเราใช้

ในบทความนี้ ฉันต้องการแบ่งปันประสบการณ์ของฉันในการใช้เทคโนโลยี SNI ที่เข้ารหัส (ESNI) ในโครงสร้างพื้นฐานของเว็บไซต์สาธารณะ

วิธีปกป้องเว็บไซต์สาธารณะของคุณด้วย ESNI

การใช้เทคโนโลยีนี้จะช่วยเพิ่มระดับความปลอดภัยเมื่อทำงานกับเว็บไซต์สาธารณะ และสอดคล้องกับมาตรฐานความปลอดภัยภายในที่บริษัทนำมาใช้

ก่อนอื่นฉันอยากจะชี้ให้เห็นว่าเทคโนโลยีไม่ได้มาตรฐานและยังอยู่ในร่าง แต่ CloudFlare และ Mozilla รองรับแล้ว (ใน ร่าง01). สิ่งนี้เป็นแรงบันดาลใจให้เราทำการทดลองเช่นนี้

ทฤษฎีเล็กน้อย

เอสนี่ เป็นส่วนขยายของโปรโตคอล TLS 1.3 ที่อนุญาตการเข้ารหัส SNI ในข้อความ TLS handshake "Client Hello" นี่คือลักษณะของ Client Hello พร้อมการสนับสนุน ESNI (แทนที่จะเป็น SNI ปกติที่เราเห็น ESNI):

วิธีปกป้องเว็บไซต์สาธารณะของคุณด้วย ESNI

 หากต้องการใช้ ESNI คุณต้องมีสามองค์ประกอบ:

  • ดีเอ็นเอส; 
  • การสนับสนุนลูกค้า
  • การสนับสนุนฝั่งเซิร์ฟเวอร์

DNS

คุณต้องเพิ่มระเบียน DNS สองรายการ – Aและ TXT (บันทึก TXT มีคีย์สาธารณะซึ่งไคลเอ็นต์สามารถเข้ารหัส SNI ได้) - ดูด้านล่าง นอกจากนี้ยังต้องมีการสนับสนุน กระทรวงกลาโหม (DNS ผ่าน HTTPS) เนื่องจากไคลเอนต์ที่มีอยู่ (ดูด้านล่าง) ไม่ได้เปิดใช้งานการสนับสนุน ESNI โดยไม่มี DoH นี่เป็นตรรกะ เนื่องจาก ESNI หมายถึงการเข้ารหัสชื่อของทรัพยากรที่เรากำลังเข้าถึง กล่าวคือ การเข้าถึง DNS ผ่าน UDP นั้นไม่สมเหตุสมผล อีกทั้งการใช้งาน DNSSEC ช่วยให้คุณป้องกันการโจมตีพิษแคชในสถานการณ์นี้

สามารถใช้งานได้ ผู้ให้บริการ DoH หลายรายในหมู่พวกเขา:

CloudFlare รัฐ (ตรวจสอบเบราว์เซอร์ของฉัน → SNI ที่เข้ารหัส → เรียนรู้เพิ่มเติม) ว่าเซิร์ฟเวอร์ของพวกเขารองรับ 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 นอกจากนี้ โปรโตคอลการสื่อสารที่มีอัปสตรีมอาจเป็น HTTP หรือ HTTPS ที่มี TLS เวอร์ชันต่ำกว่า 1.3 (หากอัปสตรีมไม่รองรับ 1.3) โครงการนี้ให้ความยืดหยุ่นสูงสุด

การดำเนินการสนับสนุน ESNI บน go เรายืมมาจาก CloudFlare. ฉันต้องการทราบทันทีว่าการใช้งานนั้นค่อนข้างไม่สำคัญ เนื่องจากเกี่ยวข้องกับการเปลี่ยนแปลงในไลบรารีมาตรฐาน การเข้ารหัสลับ/tls และจึงต้องมีการ "แพตช์" โกรูท ก่อนประกอบ.

เพื่อสร้างคีย์ ESNI ที่เราใช้ เอสนิทูล (ยังเป็นผลงานของ 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 Reverse Proxy และไม่ใช้ เรา "เท" การรับส่งข้อมูลในพื้นที่เพื่อกำจัด "การรบกวน" ในส่วนประกอบระดับกลาง

ดังนั้น ด้วยการสนับสนุน ESNI และการใช้พร็อกซีไปยังอัปสตรีมจาก HTTP เราได้ประมาณ ~550 rps จากอินสแตนซ์เดียว โดยมีปริมาณการใช้ CPU/RAM โดยเฉลี่ยของพร็อกซีย้อนกลับ ESNI:

  • การใช้งาน CPU 80% (4 vCPU, โฮสต์ RAM 4 GB, Linux)
  • RSS หน่วยความจำ 130 MB

วิธีปกป้องเว็บไซต์สาธารณะของคุณด้วย ESNI

สำหรับการเปรียบเทียบ RPS สำหรับ nginx upstream เดียวกันโดยไม่มีการยกเลิก 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, โฮสต์ RAM 4 GB, Linux) และในความเป็นจริง RPS ที่เป็นไปได้นั้นสูงกว่า (เราได้รับตัวเลขสูงถึง 2700 RPS จากทรัพยากรที่มีประสิทธิภาพมากขึ้น)

โดยสรุปฉันทราบ ว่าเทคโนโลยี ESNI ดูมีแนวโน้มดีทีเดียว ยังคงมีคำถามเปิดอยู่มากมาย เช่น ปัญหาในการจัดเก็บคีย์ ESNI สาธารณะใน DNS และการหมุนเวียนคีย์ ESNI - ปัญหาเหล่านี้กำลังถูกพูดคุยกันอย่างกระตือรือร้นและฉบับร่าง ESNI เวอร์ชันล่าสุด (ณ เวลาที่เขียน) ก็มีอยู่แล้ว 7.

ที่มา: will.com

เพิ่มความคิดเห็น