</>Học Dev
Bài học

Tuần 3 - Ngày 14: TLS / HTTPS

Tuần 3 – Ngày 14

Mục tiêu học tập

  • Hiểu vì sao HTTPS là bắt buộc cho mọi web app (eavesdropping, MITM, integrity)
  • Nắm TLS handshake — so sánh TLS 1.2 và TLS 1.3
  • Phân biệt cipher suite, key exchange (ECDHE), forward secrecy
  • Hiểu certificate chain: root CA → intermediate → leaf, cách trình duyệt verify
  • Biết Let's Encrypt và ACME protocol — auto-renew với certbot/Caddy
  • Hiểu HSTS preload và lý do HPKP bị deprecate
  • Nắm cơ bản mTLS (mutual TLS) cho service-to-service auth

1. Tại sao HTTPS bắt buộc

Ba mối đe doạ HTTPS phòng chống

BrowserHTTPplaintextServerAichenvàođưngtruynnàycóth:1.Eavesdropping(nghelén):đcpassword,cookie,content2.MITM(man-in-the-middle):modanhserver,thuthpsecret3.Tampering:saresponse(injectmalware,ads)

Cụ thể từng mối đe doạ

Mối đe doạVí dụ thực tếHTTPS giải quyết bằng
EavesdroppingWi-Fi công cộng nghe HTTP traffic, lấy session cookieEncryption (symmetric cipher)
MITMISP/coffee shop chen vào, fake server responseServer authentication (certificate)
TamperingISP inject quảng cáo vào HTML, malware vào JSIntegrity (MAC/AEAD)

Bonus: Tin cậy của user

  • Browser hiển thị "Not Secure" trên HTTP → user mất niềm tin
  • Một số API browser chỉ hoạt động trên HTTPS: Service Worker, WebRTC, Geolocation, Notifications, Web Crypto, HTTP/2, HTTP/3
  • SEO: Google rank HTTPS site cao hơn

2. TLS handshake — TLS 1.2 vs TLS 1.3

TLS 1.2 (2008)

ClientServerClientHello(supportedciphersuites,random,SNI)ServerHelloCertificateServerKeyExchangeServerHelloDoneClientKeyExchangeChangeCipherSpecFinishedChangeCipherSpecFinishedApplicationData(encrypted)ApplicationData(encrypted)2round-trips(2RTT)trưckhigidata

TLS 1.3 (2018)

ClientServerClientHello(cipher,random,SNI,key_share,ALPN)ServerHello{EncryptedExtensions}{Certificate}{CertificateVerify}{Finished}{Finished}ApplicationData(encrypted)ApplicationData(encrypted)1round-trip(1RTT)nhanhhơn50%0-RTTkhiresumesession(earlydata,córiroreplay)

Bảng so sánh

Tính chấtTLS 1.2TLS 1.3
Năm ra mắt20082018 (RFC 8446)
Handshake RTT2 RTT1 RTT (0-RTT khi resume)
Cipher suites~37 suite, nhiều legacy5 suite, chỉ AEAD
Key exchangeRSA, DH, ECDHEChỉ (EC)DHE — forward secrecy bắt buộc
Forward secrecyOptionalMandatory
Legacy cryptoCho phép RC4, 3DES, MD5, SHA-1Loại bỏ hoàn toàn
Encrypted handshakeKhông (Certificate gửi plaintext)Có (từ EncryptedExtensions trở đi)

3. Cipher suite

Cipher suite định nghĩa các thuật toán dùng trong TLS connection.

TLS 1.2 cipher suite name

TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384PRF/MAC:SHA384Encryption:AES-256-GCM(AEAD)(legacy,khôngdùngchoAEAD)Authentication:RSAsignatureKeyexchange:ECDHE(forwardsecrecy)

TLS 1.3 cipher suite (gọn hơn nhiều)

TLS_AES_256_GCM_SHA384              ← bulk + HKDF
TLS_CHACHA20_POLY1305_SHA256        ← cho ARM/mobile (no AES-NI)
TLS_AES_128_GCM_SHA256

Key exchange và signature không nằm trong cipher suite ở TLS 1.3 — được negotiate riêng (signature algorithm extension).

Khuyến nghị cipher (2024-2025)

GOOD (TLS 1.3):
  TLS_AES_256_GCM_SHA384
  TLS_AES_128_GCM_SHA256
  TLS_CHACHA20_POLY1305_SHA256

GOOD (TLS 1.2):
  ECDHE-ECDSA-AES256-GCM-SHA384
  ECDHE-RSA-AES256-GCM-SHA384
  ECDHE-ECDSA-CHACHA20-POLY1305

BAD — disable:
  - Anything với RC4, 3DES, DES, MD5, SHA-1, NULL
  - Static RSA key exchange (no forward secrecy)
  - TLS 1.0, TLS 1.1, SSL 3.0, SSL 2.0

4. Forward Secrecy

Ý tưởng

Nếu server private key bị compromise trong tương lai, traffic đã ghi lại trước đó không giải mã được.

Không có forward secrecy (RSA key exchange):
   Client encrypt session key bằng server public key → gửi qua mạng
   Attacker ghi lại traffic
   1 năm sau: server private key bị leak
   Attacker decrypt session key cũ → decrypt mọi traffic cũ

Có forward secrecy (ECDHE):
   Client + Server tạo session key qua Diffie-Hellman ephemeral
   Mỗi session có key pair mới, vứt đi sau handshake
   Server long-term key chỉ dùng để SIGN, không encrypt session key
   Server key leak → KHÔNG giúp giải mã traffic cũ

ECDHE flow đơn giản

1. Server có long-term key pair (RSA hoặc ECDSA) cho IDENTITY (sign)
2. Mỗi handshake:
   - Server tạo ephemeral EC key pair (eS_priv, eS_pub)
   - Client tạo ephemeral EC key pair (eC_priv, eC_pub)
   - Trao đổi public key
   - Cả hai compute: shared_secret = ECDH(eS_priv, eC_pub) = ECDH(eC_priv, eS_pub)
   - Server SIGN ephemeral pub key bằng long-term key (chứng minh identity)
3. Sau handshake: vứt eS_priv, eC_priv → không ai có thể tái tạo shared_secret

TLS 1.3 bắt buộc forward secrecy — đây là lý do RSA key exchange bị loại bỏ hoàn toàn.


5. Certificate chain

Cấu trúc tin cậy

RootCA(self-signed,cótrongOS/browserstore)vd:ISRGRootX1,DigiCertGlobalRootsignIntermediateCAvd:Let'sEncryptR3,DigiCertTLSRSASHA256signLeafcertificate(servercertificate)CN:example.comSAN:*.example.com,www.exam...

Browser verify như thế nào?

1. Server gửi: [leaf cert, intermediate cert] trong ServerHello
2. Browser:
   a. Đọc leaf cert → kiểm tra Subject Alt Name có match hostname không
   b. Kiểm tra leaf cert còn hạn (notBefore < now < notAfter)
   c. Verify chữ ký của intermediate trên leaf
   d. Lấy intermediate cert → verify chữ ký của root trên intermediate
   e. Root cert phải có trong trust store của OS/browser
   f. Kiểm tra revocation:
      - CRL (Certificate Revocation List) — list bị thu hồi
      - OCSP (Online Certificate Status Protocol)
      - OCSP stapling — server đính kèm OCSP response
3. Nếu mọi bước OK → "Secure" lock icon

Kiểm tra chain với openssl

# Lấy certificate chain từ server
openssl s_client -connect example.com:443 -showcerts

# Verify chain
openssl s_client -connect example.com:443 -CApath /etc/ssl/certs

# In ra detail của leaf cert
echo | openssl s_client -connect example.com:443 2>/dev/null | \
  openssl x509 -text -noout

# Check expiration
echo | openssl s_client -connect example.com:443 2>/dev/null | \
  openssl x509 -enddate -noout
# notAfter=Mar 15 23:59:59 2026 GMT

Lỗi chain phổ biến

LỗiTriệu chứngNguyên nhân
unable to get local issuer certificateBrowser/curl không tin certServer không gửi intermediate cert
NET::ERR_CERT_DATE_INVALIDCert hết hạnQuên renew
NET::ERR_CERT_COMMON_NAME_INVALIDHostname mismatchCN/SAN không có domain đang truy cập
NET::ERR_CERT_AUTHORITY_INVALIDBrowser không tin rootSelf-signed cert hoặc root không có trong store

6. Let's Encrypt và ACME protocol

Let's Encrypt là gì

CA miễn phí, automated, được hỗ trợ bởi Internet Security Research Group (ISRG). Cert valid 90 ngày, khuyến khích renew sau 60 ngày.

ACME protocol (RFC 8555)

Client(certbot)ACMEserver(Let'sEncrypt)certbot1.Neworder:example.comACMEcertbot2.Challenge(HTTP-01hocDNS-01)ACME3.Clientđápngchallenge:HTTP-01:servefiletihttp://example.com/.well-known/acme-challenge/TOKENDNS-01:thêmTXTrecordvàoDNScertbot4.NotifyreadyACMEcertbot5.Verifychallenge(fetchURL/DNS)ACMEcertbot6.CSR(certificatesigningreq)ACMEcertbot7.Cert+chainACME

certbot — cách phổ biến nhất

# Cài certbot
sudo apt install certbot python3-certbot-nginx

# Standalone (cert + nginx config auto)
sudo certbot --nginx -d example.com -d www.example.com

# DNS-01 (cho wildcard cert)
sudo certbot certonly --manual --preferred-challenges dns -d "*.example.com"

# Auto-renew (cron tự setup)
sudo certbot renew --dry-run

Cron job mặc định:

0 */12 * * * root certbot renew --quiet

Caddy — đơn giản nhất

# Caddyfile
example.com {
    reverse_proxy localhost:3000
}

Đó là tất cả. Caddy tự xin Let's Encrypt cert, tự renew, tự redirect HTTP→HTTPS, tự config HTTPS đúng chuẩn. Không cần nginx + certbot dance.

nginx + certbot snippet

server {
    listen 443 ssl http2;
    server_name example.com;

    ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_prefer_server_ciphers off;
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 1d;

    # HSTS
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;

    location / {
        proxy_pass http://localhost:3000;
    }
}

# Redirect HTTP → HTTPS
server {
    listen 80;
    server_name example.com;
    return 301 https://$host$request_uri;
}

7. HSTS và HPKP

HSTS (HTTP Strict Transport Security)

Response header:
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload

Ý nghĩa: "Browser, từ giờ trong 1 năm, chỉ kết nối với tôi qua HTTPS — kể cả khi user gõ http://".

HSTS preload list

Vấn đề first-visit: lần đầu vào site, browser chưa thấy HSTS header → có thể bị MITM trên HTTP. Giải pháp: preload list.

1. Site đăng ký tại https://hstspreload.org
2. Chrome, Firefox, Safari ship sẵn danh sách HSTS preload trong browser
3. Kể cả lần đầu vào site, browser auto upgrade HTTP→HTTPS

Yêu cầu preload:

  • Cert valid
  • HTTPS hoạt động trên toàn domain và subdomain
  • max-age >= 31536000 (1 năm)
  • includeSubDomains; preload directive

Cảnh báo: preload khó undo. Nếu vài tháng sau bạn muốn tắt HTTPS trên subdomain nào đó, phải đợi vài tháng để remove khỏi list.

HPKP (HTTP Public Key Pinning) — deprecated

Ý tưởng: server gửi header pin một public key. Browser nhớ pin, từ chối cert nào không match pin.

Public-Key-Pins: pin-sha256="base64=="; max-age=2592000;

Tại sao bị deprecate (2017)?

  1. Foot-gun lớn: pin sai key → site không truy cập được cho user đã visit, kể cả khi đã reissue cert. Một số site đã tự khoá mình hàng tháng.
  2. Ransomware vector: attacker compromise server tạm thời → pin key giả → server không thể recover.
  3. Certificate Transparency (CT) ra đời, giải quyết phần lớn vấn đề "rogue CA" mà HPKP nhắm tới.

Chrome bỏ HPKP support năm 2018. Thay thế: Expect-CT header (nay cũng deprecated) và CAA DNS record.

CAA record — phòng tuyến hiện tại

example.com.    IN  CAA  0 issue "letsencrypt.org"
example.com.    IN  CAA  0 issuewild ";"
example.com.    IN  CAA  0 iodef "mailto:security@example.com"

Ý nghĩa: chỉ Let's Encrypt được phép issue cert cho example.com. Mọi CA tuân thủ CA/Browser Forum baseline phải check CAA trước khi issue.


8. mTLS (mutual TLS)

TLS thường vs mTLS

TLS thường:
   Client verify Server identity (qua cert)
   Server không verify Client identity
   → App-level auth (cookie, JWT, OAuth) để biết client là ai

mTLS:
   Client verify Server identity
   Server cũng verify Client identity (qua client cert)
   → Auth ngay tại transport layer, không cần app-level token

Khi nào dùng mTLS

  • Service-to-service trong microservices (vd: service mesh — Istio, Linkerd)
  • B2B API giữa các công ty (cert thay vì shared secret)
  • IoT — thiết bị có cert cá biệt, không phù hợp với username/password
  • Zero Trust network — mọi connection phải auth, kể cả internal

Setup mTLS đơn giản với nginx

server {
    listen 443 ssl;
    server_name api.example.com;

    ssl_certificate     /etc/ssl/server.crt;
    ssl_certificate_key /etc/ssl/server.key;

    # Yêu cầu client cert
    ssl_client_certificate /etc/ssl/ca.crt;   # CA tin cậy cho client cert
    ssl_verify_client on;                      # bắt buộc client cert

    location / {
        # Forward client cert info xuống upstream
        proxy_set_header X-Client-CN $ssl_client_s_dn_cn;
        proxy_set_header X-Client-Cert $ssl_client_escaped_cert;
        proxy_pass http://backend;
    }
}

Service mesh: auto mTLS

Trong Kubernetes với Istio/Linkerd:

apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
  name: default
spec:
  mtls:
    mode: STRICT

Istio inject sidecar (Envoy) vào mỗi pod. Sidecar handle TLS handshake với cert được cấp bởi Istio CA. App code không cần biết về TLS — Envoy lo hết.

Pattern client cert authentication trong code (Node.js)

import https from "node:https";

const options = {
  hostname: "api.partner.com",
  port: 443,
  path: "/data",
  method: "GET",
  cert: fs.readFileSync("./client-cert.pem"),
  key:  fs.readFileSync("./client-key.pem"),
  ca:   fs.readFileSync("./partner-ca.pem"),  // verify server
};

const req = https.request(options, (res) => {
  console.log(`Status: ${res.statusCode}`);
});
req.end();

9. Câu hỏi ôn tập

  1. Tại sao TLS 1.3 nhanh hơn TLS 1.2 trong handshake?

    Xem đáp án

    TLS 1.3 giảm từ 2 RTT xuống 1 RTT trước khi client gửi được application data, và hỗ trợ 0-RTT khi resume session. Lý do: TLS 1.3 loại bỏ negotiation 2 chiều phức tạp — client gửi key_share ngay trong ClientHello (đoán cipher), server có đủ info để compute key và trả luôn certificate + Finished trong ServerHello. Ngoài ra, TLS 1.3 mã hoá phần lớn handshake (từ EncryptedExtensions), nâng cao privacy.

  2. Forward secrecy nghĩa là gì, và tại sao TLS 1.3 bắt buộc?

    Xem đáp án

    Forward secrecy: nếu server long-term private key bị compromise trong tương lai, traffic đã được ghi lại trước đó vẫn không giải mã được. Đạt được bằng cách dùng ephemeral Diffie-Hellman (ECDHE) — mỗi session tạo key pair tạm, vứt đi sau handshake; long-term key chỉ dùng để sign, không encrypt session key.

    TLS 1.3 loại bỏ static RSA key exchange (không có forward secrecy) và chỉ cho phép (EC)DHE — vì vậy forward secrecy mặc định bật cho mọi connection.

  3. Khi browser nhận được leaf certificate, nó verify như thế nào để quyết định "trusted"?

    Xem đáp án

    (1) Hostname đang truy cập phải match Subject Alt Name (SAN) của cert. (2) Thời gian hiện tại nằm giữa notBeforenotAfter. (3) Chữ ký của intermediate CA trên leaf cert valid. (4) Đi ngược chain: chữ ký của root CA trên intermediate valid. (5) Root CA phải có trong trust store của OS/browser. (6) Kiểm tra revocation qua CRL, OCSP, hoặc OCSP stapling.

    Nếu một trong các bước fail → browser hiển thị cảnh báo "Not Secure".

  4. Vì sao HPKP bị deprecated, và cơ chế nào thay thế nó?

    Xem đáp án

    HPKP cho phép server pin public key vào browser — browser nhớ pin và từ chối cert nào không match. Vấn đề: (1) pin sai key = tự khoá site khỏi tất cả user đã visit, không revert được trong thời gian max-age. (2) Attacker chiếm server tạm thời có thể pin key giả → ransomware. (3) Hiệu ứng yếu so với rủi ro vận hành.

    Thay thế: Certificate Transparency (CT logs — mọi cert public phải log, dễ phát hiện rogue CA) và CAA DNS record (chỉ định CA nào được phép issue cert cho domain). Cả hai đều an toàn hơn HPKP về mặt vận hành.

  5. mTLS khác TLS thường ở điểm gì, và khi nào nên dùng?

    Xem đáp án

    TLS thường chỉ verify identity của server (qua cert). mTLS verify cả client (client cũng có cert, server check chain).

    Nên dùng mTLS khi: (1) service-to-service trong microservices/service mesh — auth ở transport layer thay vì share token, (2) B2B API giữa các tổ chức — cert cấp cho từng partner, (3) IoT — thiết bị có cert cá biệt thay vì username/password, (4) Zero Trust — mọi connection nội bộ vẫn phải auth.

    Trade-off: phức tạp hơn (cert lifecycle, distribution), không phù hợp cho public-facing user app (user không có client cert).

Bài tập thực hành

# 1. Kiểm tra cert của một site
openssl s_client -connect github.com:443 -servername github.com < /dev/null 2>/dev/null | \
  openssl x509 -text -noout | head -30

# 2. Xem cipher suite được dùng
openssl s_client -connect github.com:443 < /dev/null 2>&1 | grep -E "Protocol|Cipher"

# 3. Test với TLS 1.3 only
openssl s_client -connect github.com:443 -tls1_3 < /dev/null

# 4. Generate self-signed cert cho local dev
openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem \
  -sha256 -days 365 -nodes \
  -subj "/CN=localhost" \
  -addext "subjectAltName=DNS:localhost,DNS:127.0.0.1"

# 5. Generate CA + client cert cho mTLS demo
# Tạo CA
openssl genrsa -out ca.key 4096
openssl req -x509 -new -nodes -key ca.key -days 365 -out ca.crt -subj "/CN=My CA"

# Tạo client key + CSR
openssl genrsa -out client.key 2048
openssl req -new -key client.key -out client.csr -subj "/CN=client-1"

# CA sign client CSR
openssl x509 -req -in client.csr -CA ca.crt -CAkey ca.key -CAcreateserial \
  -out client.crt -days 365

# 6. Setup Caddy với auto-HTTPS (cần domain thật + public IP)
cat > Caddyfile <<EOF
example.com {
    reverse_proxy localhost:3000
}
EOF
caddy run

# 7. SSL Labs test (online, comprehensive)
# Mở: https://www.ssllabs.com/ssltest/analyze.html?d=your-site.com
# Mục tiêu: A+ rating

Tài liệu tham khảo chính thức


Tiếp theo: Ngày 15 — Encryption at Rest & in Transit