TLS/SSL: Mã Hóa và Chứng Chỉ

K
Kai··6 min read

Bài 8 nói HTTPS là HTTP gói trong TLS. Bài này mở chiếc hộp TLS: nó mã hóa thế nào, "chứng chỉ" là gì, và làm sao trình duyệt biết một website đáng tin. Đây là phần nền của bảo mật web, và là nguồn của lỗi quen thuộc "chứng chỉ hết hạn / không hợp lệ".

Tên gọi: SSL là tên cũ (đã lỗi thời, không nên dùng nữa), TLS là phiên bản hiện đại thay thế. Người ta vẫn quen miệng gọi "SSL" nhưng thực tế là TLS. Phiên bản mới nhất là TLS 1.3 (RFC 8446) — nhanh và an toàn hơn.

TLS đảm bảo ba điều

Nhắc lại từ Bài 8, TLS cho ba thứ:

  • Bí mật (confidentiality) — nội dung được mã hóa, người chen giữa đường (Bài 3) không đọc được.
  • Toàn vẹn (integrity) — dữ liệu không bị sửa trên đường mà không bị phát hiện.
  • Xác thực (authentication) — bạn chắc đang nói chuyện với đúng example.com, không phải kẻ giả mạo.

Hai cái đầu là "mã hóa", cái thứ ba là "chứng chỉ" — và cái thứ ba mới là phần tinh tế.

Hai loại mã hóa, dùng cho hai việc

TLS dùng cả hai loại mã hóa, mỗi loại cho một mục đích:

  • Bất đối xứng (asymmetric) — mỗi bên có một cặp khóa công khai/riêng tư. Mã bằng khóa này, giải bằng khóa kia. Mạnh nhưng chậm. TLS dùng nó lúc bắt tay để hai bên thỏa thuận an toàn một khóa chung.
  • Đối xứng (symmetric) — cùng một khóa để mã và giải. Nhanh. TLS dùng nó để mã hóa dữ liệu thật sau khi bắt tay.

Ý tưởng: dùng bất đối xứng (chậm nhưng an toàn) chỉ để trao đổi một khóa đối xứng (gọi là session key), rồi dùng khóa đối xứng (nhanh) cho toàn bộ dữ liệu. Tận dụng ưu điểm cả hai.

Bắt tay TLS

Sau khi TCP đã kết nối (Bài 6), TLS bắt tay để thiết lập kênh mã hóa (TLS 1.3, rút gọn):

   Client                                      Server
     │ ── ClientHello ─────────────────────────► │  "tôi hỗ trợ TLS 1.3, các cipher này..."
     │ ◄─ ServerHello + Certificate + key ──────── │  "chọn cipher X; đây là chứng chỉ của tôi"
     │    (1) kiểm tra chứng chỉ (chuỗi tin cậy — mục dưới)
     │    (2) cả hai tính ra cùng một session key đối xứng
     │ ══════════ kênh mã hóa sẵn sàng ══════════ │
     │ ◄──────── dữ liệu HTTP đã mã hóa ─────────► │

TLS 1.3 gộp bước, chỉ tốn một vòng đi-về (1-RTT) — nhanh hơn hẳn các phiên bản cũ. Quan sát thật:

echo | openssl s_client -connect example.com:443 -servername example.com
New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384
Verify return code: 0 (ok)

TLSv1.3 là phiên bản, TLS_AES_256_GCM_SHA384 là bộ cipher đã thỏa thuận (AES-256 cho mã hóa đối xứng), Verify return code: 0 (ok) nghĩa là chứng chỉ hợp lệ.

Chứng chỉ: chứng minh "tôi đúng là example.com"

Mã hóa thôi chưa đủ — nếu bạn mã hóa nhưng đang nói chuyện với kẻ giả mạo thì vô nghĩa. Cần xác thực server. Đó là việc của chứng chỉ số (certificate).

Chứng chỉ là một file gắn khóa công khai của server với danh tính của nó, và được một bên thứ ba tin cậy (CA — Certificate Authority) . Xem chứng chỉ thật:

echo | openssl s_client -connect example.com:443 -servername example.com \
  | openssl x509 -noout -subject -issuer -dates
subject=CN = example.com                                   ← cấp cho ai
issuer=O = "CLOUDFLARE, INC.", CN = Cloudflare TLS ... CA   ← ai cấp (CA)
notBefore=Apr  2 ... 2026 GMT                               ← hiệu lực từ
notAfter=Jul  1 ... 2026 GMT                                ← hết hạn

Để ý notAfter — chứng chỉ có hạn. Quên gia hạn là lỗi "your connection is not private" kinh điển. (Dịch vụ như Let's Encrypt cấp chứng chỉ miễn phí và tự gia hạn để tránh việc này.)

Chuỗi tin cậy: vì sao trình duyệt tin

Trình duyệt làm sao biết chứng chỉ của example.com là thật? Nó không tin trực tiếp — nó tin theo một chuỗi (chain of trust) lần ngược lên một CA gốc (Root CA) mà nó đã tin sẵn.

echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null \
  | grep -E "s:|i:"
 0 s:CN = example.com                          ← chứng chỉ server
   i:CN = Cloudflare TLS Issuing ECC CA 1       ← do CA trung gian này ký
 1 s:CN = Cloudflare TLS Issuing ECC CA 1        ← CA trung gian
   i:CN = SSL.com TLS Transit ECC CA R2          ← do CA cấp cao hơn ký
 2 s:CN = SSL.com TLS Transit ECC CA R2
   i:CN = SSL.com TLS ECC Root CA 2022           ← tới ROOT CA

Đọc s: (subject — cấp cho ai) và i: (issuer — ai ký):

   Root CA (SSL.com Root 2022)   ← nằm sẵn trong "trust store" của OS/trình duyệt, tin tuyệt đối
        │ ký cho
        ▼
   CA trung gian (SSL.com Transit → Cloudflare CA)
        │ ký cho
        ▼
   Chứng chỉ example.com   ◄── server trình ra khi bắt tay

Trình duyệt lần theo chuỗi chữ ký: chứng chỉ example.com do Cloudflare CA ký, Cloudflare CA do SSL.com ký, SSL.com Root nằm trong danh sách Root CA mà hệ điều hành/trình duyệt tin sẵn. Lần được tới một Root tin cậy = hợp lệ (Verify return code: 0). Nếu chuỗi đứt, sai tên, hay hết hạn → trình duyệt cảnh báo.

Đây là lý do bạn không thể tự ký chứng chỉ cho google.com: chứng chỉ tự ký không lần được tới Root CA nào trình duyệt tin, nên bị cảnh báo ngay.

Liên hệ thực tế DevOps

  • Hết hạn chứng chỉ là sự cố production phổ biến — đặt cảnh báo, dùng tự động gia hạn (Let's Encrypt + certbot — chính series blog này dùng certbot, nhớ không).
  • Chứng chỉ tự ký dùng được trong nội bộ/dev, nhưng phải thêm CA của mình vào trust store để khỏi cảnh báo.
  • TLS termination: thường load balancer/reverse proxy (Bài 11) lo giải mã TLS, rồi nói HTTP thường với backend bên trong — gọi là "TLS termination".

Tổng kết

TLS (tên mới của SSL; bản hiện hành TLS 1.3) cho HTTPS ba đảm bảo: bí mật, toàn vẹn, xác thực. Nó dùng mã hóa bất đối xứng lúc bắt tay để thỏa thuận một khóa đối xứng, rồi dùng khóa đó mã hóa dữ liệu (nhanh). Chứng chỉ gắn khóa công khai với danh tính server, được CA ký; trình duyệt tin server bằng cách lần chuỗi tin cậy từ chứng chỉ lên một Root CA tin sẵn. openssl s_client cho bạn soi cả phiên bản, cipher, và chuỗi chứng chỉ. Nhớ: chứng chỉ có hạn — tự động gia hạn để khỏi sập.

Ta đã đi hết hành trình một request (DNS → TCP → TLS → HTTP). Ba bài tới chuyển sang góc vận hành: kiểm soát truy cập (firewall), mở rộng (load balancer), và chẩn đoán. Bài 10: firewall và bảo mật mạng.