[Hành trình tự học DevOps] Bài 2. HTTPS - không chỉ là mã hoá
Bài viết được biên soạn dựa trên nội dung video How HTTPS Works - It's Not Just Encryption của LearnThatStack, với mục đích tổng hợp và trình bày lại các khái niệm một cách dễ tiếp cận hơn.
🔐 Chiếc ổ khóa nhỏ trên trình duyệt
Chiếc ổ khóa nhỏ đó — bạn đã nhìn thấy nó hàng nghìn lần.
Bạn từng hướng dẫn người dùng tìm biểu tượng này, từng thiết lập HTTPS cho ứng dụng của mình. Bạn cũng từng gặp những trang báo lỗi màu đỏ, bấm “Proceed anyway” khi chạy localhost rồi tiếp tục công việc.
Nhưng HTTPS thực sự hoạt động như thế nào?
Hầu hết chúng ta thường hình dung nó như thế này:
Browser lấy key của server, dùng key đó để mã hóa dữ liệu, sau đó server giải mã dữ liệu ở đầu bên kia.
Nhưng thực tế không phải như vậy.
HTTPS không chỉ đơn giản là mã hóa dữ liệu.
Chúng ta sẽ tìm hiểu:
- TLS đang bảo vệ dữ liệu của chúng ta như thế nào.
- Certificate và CA giúp browser xác thực server ra sao.
- Khi TLS hoặc certificate bị lỗi, chúng ta debug ở đâu.
Phần 1. TLS đang bảo vệ chúng ta khỏi điều gì?
Trước khi đi vào handshake, chúng ta cần hiểu chính xác TLS đang cố bảo vệ điều gì.
TLS giải quyết ba vấn đề chính.
1.1. Confidentiality — Tính bí mật
Câu hỏi là:
Người khác có thể đọc dữ liệu đang truyền hay không?
Khi bạn gửi một HTTP request, nó không đi thẳng từ laptop đến server.
Nó có thể đi qua:
Laptop
|
Router
|
ISP
|
Internet
|
Server
Bạn không sở hữu và cũng không kiểm soát phần lớn cơ sở hạ tầng này.
Nếu dữ liệu được truyền dưới dạng plaintext, attacker có khả năng quan sát network có thể đọc được những gì bạn gửi.
TLS mã hóa dữ liệu để attacker không thể đọc nội dung của connection.
1.2. Integrity — Tính toàn vẹn
Câu hỏi thứ hai:
Người khác có thể sửa dữ liệu mà bạn không phát hiện ra hay không?
Ví dụ, bạn gửi:
Chuyển 100 USD
Nếu attacker có thể sửa request thành:
Chuyển 1000 USD
mà cả client và server đều không biết thì encryption không còn đủ.
Vì vậy TLS cũng phải đảm bảo:
Dữ liệu nhận được không bị thay đổi mà các bên không phát hiện.
1.3. Authentication — Xác thực
Đây mới là phần rất quan trọng.
Câu hỏi thứ ba:
Server mà bạn đang nói chuyện có thực sự là server mà nó tuyên bố hay không?
Giả sử tôi có thể khiến browser của bạn kết nối tới server của tôi thay vì server ngân hàng:
Browser
|
HTTPS
v
Attacker
Connection vẫn có thể được mã hóa.
Nhưng bạn đang gửi credential cho attacker.
Vì vậy:
Encryption mà không có authentication thì chưa phải security.
Nó chỉ là:
Privacy dành cho nhầm người.
Hãy ghi nhớ ba mục tiêu này:
Confidentiality + Integrity + Authentication
Đây chính là nền tảng để hiểu toàn bộ TLS.
Phần 2. TLS sử dụng những cơ chế nào?
Bây giờ chúng ta biết TLS cần giải quyết ba vấn đề.
Nhưng để làm được điều đó, TLS không chỉ sử dụng một loại cryptography.
Nó kết hợp nhiều cơ chế khác nhau.
2.1. Symmetric Encryption
TLS sử dụng symmetric encryption để mã hóa dữ liệu thực tế của session.
Với symmetric encryption, hai bên sử dụng cùng một key:
Same Key
|
+----+----+
| |
Encrypt Decrypt
Một bên dùng key để encrypt.
Bên kia dùng chính key đó để decrypt.
AES là một symmetric cipher phổ biến và rất nhanh.
Đây là loại encryption phù hợp để xử lý lượng dữ liệu lớn của một HTTP session.
Nhưng chúng ta gặp một vấn đề:
Hai bên phải có cùng một key.
Bây giờ xuất hiện một câu hỏi rất thú vị:
Làm sao hai bên có thể tạo ra cùng một shared secret thông qua một channel hoàn toàn công khai?
Chúng ta cần một cách để hai bên có thể tạo ra cùng một secret mà không cần gửi secret đó qua network.
2.2. Public-key Cryptography
Public-key cryptography được phát triển vào những năm 1970 để giải quyết vấn đề này.
Thay vì chỉ sử dụng một secret key chung, mỗi bên có thể sở hữu một key pair:
Public Key
+
Private Key
Private key được giữ bí mật, trong khi public key có thể được công khai.
Hai key có quan hệ toán học với nhau, nhưng với các thuật toán hiện đại, việc suy ra private key từ public key là không thực tế.
Điều này cho phép server công khai public key mà không làm lộ private key.
Tuy nhiên, cần phân biệt vai trò của public-key cryptography trong TLS hiện đại.
Public-key cryptography chủ yếu được dùng để xác thực server thông qua chữ ký số, thay vì trực tiếp tạo ra secret chung:
- Server sở hữu private key tương ứng với public key trong certificate.
- Trong quá trình handshake, server dùng private key để tạo chữ ký, qua đó chứng minh rằng nó thực sự sở hữu private key đó.
- Browser sử dụng public key để kiểm tra chữ ký.
Trong TLS hiện đại, việc hai bên tạo ra shared secret được thực hiện sử dụng thuật toán ECDHE.
2.3. ECDHE — Tạo Shared Secret
TLS 1.3 sử dụng:
ECDHE — Elliptic Curve Diffie-Hellman Ephemeral
Ý tưởng quan trọng nhất là:
Hai bên có thể tạo ra cùng một shared secret mà không cần truyền shared secret đó qua network.
Attacker có thể nhìn thấy các thông tin được trao đổi.
Nhưng họ không thể thực tế tính ngược ra secret nếu không giải được bài toán toán học phía sau ECDHE.
Hãy hình dung thuật toán ECDHE giống như một trò chơi pha trộn màu sơn công khai.
Bước 1. Alice và Bob thống nhất một màu gốc công khai G.
Bước 2. Mỗi người tự giữ một màu bí mật riêng. Alice giữ màu A, Bob giữ màu B.
Bước 3. Hai người trộn màu gốc với màu bí mật của mình rồi gửi cho nhau. Alice gửi màu AG, Bob gửi màu BG. Kẻ trộm chỉ thấy hai hỗn hợp này.
Bước 4. Nhận hỗn hợp của đối phương, pha thêm màu bí mật của mình để cùng nhận được một màu cuối cùng (ABG).
Màu cuối cùng này chính là Bí mật chung (Shared Secret) dùng để mã hóa dữ liệu.
Tại sao nó an toàn tuyệt đối?
Kẻ trộm dù biết toàn bộ thuật toán, có đủ màu G, AG và BG, nhưng bất khả thi để tách ngược nhằm tìm ra màu bí mật A, B của Alice và Bob.
Do đó, không thế tìm ra được màu bí mật chung.
Security không đến từ việc:
“Giấu thuật toán.”
Mà đến từ:
Một tính chất toán học khiến việc đảo ngược phép tính trở nên bất khả thi về mặt tính toán trong thực tế.
Có một từ rất quan trọng trong ECDHE:
Ephemeral
ECDHE tạo một key pair tạm thời cho từng session.
Điều này mang lại:
Forward Secrecy
Giả sử attacker ghi lại traffic của bạn hôm nay.
Nhiều năm sau, private key của server bị đánh cắp.
Với forward secrecy:
Attacker vẫn không thể dùng private key đó để giải mã traffic cũ đã ghi lại.
Đây là một trong những lý do ECDHE rất quan trọng trong TLS hiện đại, và được sử dụng thay thế cho phương pháp cũ dùng Public-key Cryptography.
2.4. Tại sao không dùng Asymmetric Encryption cho toàn bộ dữ liệu?
Các thuật toán asymmetric phù hợp cho những tác vụ như authentication và key agreement, nhưng không hiệu quả khi phải xử lý lượng dữ liệu lớn.
Vì vậy TLS sử dụng mỗi cơ chế cho một nhiệm vụ phù hợp:
Certificate
|
v
Authenticate Server
ECDHE
|
v
Shared Secret
|
v
Session Keys
|
v
Symmetric Encryption
|
v
HTTPS Data
Nói ngắn gọn:
Public-key cryptography giúp xác thực server.
ECDHE dùng để thiết lập shared secret.
Symmetric encryption dùng để mã hóa dữ liệu.
Bây giờ chúng ta đã biết từng thành phần làm gì.
Hãy xem chúng phối hợp với nhau trong một TLS handshake.
Phần 3. TLS Handshake hoạt động như thế nào?
Khi bạn nhập:
https://bank.com
và nhấn Enter, TLS handshake sẽ diễn ra trước khi HTTP request được truyền đi.
Có thể hình dung đơn giản:
Browser Server
| |
|---- Hello ----->|
|<-- Certificate -|
|<-- Key Share ---|
| |
| Session Keys |
| |
|-- HTTPS Data -->|
Bây giờ hãy đi qua từng bước.
3.1. Client Hello
Browser bắt đầu bằng:
Client Hello
Nó thông báo cho server biết những gì client hỗ trợ, chẳng hạn:
- TLS version.
- Cipher suites.
- Client random.
- Key share.
Bạn có thể hình dung browser đang nói:
“Đây là những công cụ cryptography mà tôi hỗ trợ.”
Server sẽ chọn những gì hai bên có thể sử dụng chung.
Client Hello cũng chứa:
Client Random
Đây là một giá trị random được tạo mới cho connection và được sử dụng trong quá trình tạo session keys.
Client cũng gửi:
Key Share
Đây là một phần của quá trình ECDHE.
TLS 1.3 gửi key share ngay trong Client Hello để hai bên có thể bắt đầu key exchange sớm hơn.
3.2. Server Hello và Certificate
Server phản hồi với những thông tin cần thiết cho handshake:
- Cipher suite được chọn.
- Server random.
- Server key share.
- Certificate.
Certificate rất quan trọng vì nó chứa identity và public key của server.
Nó trả lời câu hỏi:
“Server này là ai?”
Nhưng browser chưa thể đơn giản tin mọi certificate mà server gửi.
Browser phải kiểm tra:
“Tôi có tin certificate này không?”
Chúng ta sẽ quay lại phần này ở Phần 4.
3.3. Từ Shared Secret đến Session Keys
Tiếp theo, ECDHE tạo ra:
Shared Secret
Nhưng TLS cần những key cụ thể để bảo vệ connection.
TLS 1.3 sử dụng:
HKDF — HMAC-based Key Derivation Function
để dẫn xuất session keys từ các thông tin đã có trong handshake.
Có thể hình dung đơn giản:
Client Random
+
Server Random
+
Shared Secret
|
v
HKDF
|
v
Session Keys
Hai bên có cùng input nên có thể độc lập tạo ra cùng session keys mà không cần truyền những key đó qua network.
TLS còn sử dụng các key khác nhau cho từng hướng truyền dữ liệu.
Điểm cần nhớ là:
Session key không được gửi trực tiếp qua network.
Hai bên tự tạo ra chúng từ các thông tin của handshake.
3.4. Finished Message → HTTP Request
Sau khi session keys được tạo, handshake vẫn chưa hoàn toàn kết thúc.
Server gửi một:
Finished message
được bảo vệ bằng key vừa tạo.
Client kiểm tra message này.
Nếu hợp lệ, client có thể xác nhận rằng handshake không bị thay đổi và hai bên đã derive key thành công.
Client cũng gửi Finished message của mình.
Khi quá trình này hoàn tất:
TLS handshake đã hoàn thành.
Lúc này HTTP request thực sự được truyền đi:
HTTP Request
|
v
Symmetric Encryption
|
v
Encrypted HTTPS
Đây là điểm quan trọng nhất cần nhớ:
Asymmetric cryptography không phải thứ đang mã hóa toàn bộ HTTP traffic.
ECDHE giúp tạo shared secret.
HKDF giúp tạo session keys.
Sau đó symmetric encryption xử lý dữ liệu thực tế vì nó nhanh hơn rất nhiều.
TLS 1.3 được thiết kế để hoàn thành handshake với một round trip trong trường hợp thông thường, giúp giảm latency so với các phiên bản TLS cũ hơn.
Phần 4. Certificate và CA làm gì?
Đến đây chúng ta đã hiểu cách TLS tạo ra một connection được mã hóa.
Nhưng vẫn còn một câu hỏi quan trọng:
Làm sao browser biết server mà nó đang kết nối là server thật?
Đây chính là vai trò của certificate và Certificate Authority.
Có sự khác biệt rất lớn giữa:
“Connection được mã hóa.”
và:
“Connection được mã hóa tới đúng server.”
4.1. Certificate Authority — CA
Certificate Authority, viết tắt là CA, là một tổ chức mà browser và operating system đã đồng ý tin tưởng.
Khi bạn muốn có certificate cho domain của mình, bạn phải chứng minh với CA rằng:
Bạn thực sự kiểm soát domain đó.
CA có thể xác minh điều này bằng nhiều cách, chẳng hạn:
- Đặt một file tại một URL cụ thể.
- Tạo một DNS record.
Sau khi xác minh, CA cấp một certificate và ký nó bằng private key của CA.
Browser có sẵn public key của CA trong trust store của browser hoặc operating system.
Vì vậy khi browser nhận certificate cho:
bank.com
nó có thể kiểm tra:
Certificate này có được ký bởi CA mà tôi tin tưởng không?
Nếu signature hợp lệ và các điều kiện khác cũng hợp lệ, browser có cơ sở để tin certificate.
Điểm quan trọng cần nhớ:
Certificate giúp xác định identity của server.
CA giúp browser có cơ sở để tin identity đó.
4.2. Certificate Chain
Trong thực tế, Root CA thường không trực tiếp ký tất cả server certificate.
Thay vào đó, hệ thống sử dụng một chain:
Root CA
|
Intermediate CA
|
Server Certificate
Server certificate được ký bởi Intermediate CA.
Intermediate CA lại được ký bởi Root CA.
Root CA là phần mà browser hoặc operating system đã tin tưởng từ trước.
Vì vậy browser có thể đi theo chain:
Server Certificate
|
v
Intermediate CA
|
v
Root CA
|
v
Trusted
Đây gọi là:
Chain of Trust
Nếu một mắt xích trong chain có vấn đề, browser có thể reject toàn bộ certificate.
Ví dụ:
- Certificate hết hạn.
- Thiếu intermediate certificate.
- Signature không hợp lệ.
- Certificate không được trust.
- Domain không khớp.
4.3. Bên trong một X.509 Certificate
Certificate tuân theo format:
X.509
Bạn không cần nhớ toàn bộ X.509 để làm DevOps.
Chỉ cần đặc biệt chú ý đến một vài thông tin:
Issuer
Validity
SAN
Public Key
Signature
Issuer cho biết ai đã ký certificate.
Validity cho biết khoảng thời gian certificate có hiệu lực:
Not Before
Not After
Sau ngày Not After, certificate đã hết hạn.
SAN — Subject Alternative Name là một field đặc biệt quan trọng.
Nó chứa những hostname/domain mà certificate có thể đại diện.
Ví dụ certificate có thể được cấp cho:
api.example.com
nhưng bạn lại truy cập:
api.test.com
Certificate có thể hoàn toàn hợp lệ về mặt cryptography nhưng browser vẫn từ chối vì domain không khớp.
Vì vậy khi gặp lỗi:
certificate verify failed
hoặc hostname mismatch:
SAN là một trong những thứ đầu tiên cần kiểm tra.
CN — Common Name — từng được sử dụng cho hostname nhưng hiện nay không còn là field chính để browser validate domain.
Public Key của server cũng được nhúng bên trong certificate.
Private key tương ứng được server giữ bí mật.
Trong TLS, private key này được sử dụng trong quá trình xác thực handshake. Browser có thể sử dụng public key để verify signature từ server.
Cuối cùng là Signature.
Certificate chứa cryptographic signature của CA.
Nếu attacker thay đổi domain, expiry, public key hoặc nội dung được ký, signature sẽ không còn hợp lệ.
Attacker không thể tạo một signature hợp lệ nếu không có private key của CA.
Ngoài ra còn có Key Usage và Extended Key Usage, cho biết certificate được phép sử dụng vào mục đích nào.
Phần 5. Khi TLS lỗi, debug ở đâu?
Đến đây chúng ta đã hiểu handshake và certificate.
Bây giờ hãy quay lại một tình huống rất quen thuộc.
Bạn setup HTTPS cho local development.
Server đang chạy bình thường.
HTTPS cũng đang hoạt động.
Nhưng browser vẫn hiện:
Advanced
|
v
Proceed anyway
Tại sao?
5.1. Self-Signed Certificate và mkcert
Self-signed certificate là certificate được chính server tự ký.
Có thể hình dung:
Certificate
|
v
Server tự ký
Không có CA đáng tin đứng phía sau.
Điều thú vị là:
- TLS handshake vẫn có thể xảy ra.
- Key exchange vẫn có thể xảy ra.
- Data vẫn có thể được mã hóa.
Vậy thứ gì thất bại?
Authentication.
Browser không có trusted third party nào xác nhận:
“Certificate này thực sự thuộc về server đó.”
Bất kỳ attacker nào cũng có thể tự tạo một certificate cho bank.com.
Vì vậy browser không thể phân biệt:
Real bank.com
vs
Fake bank.com
Đó là lý do browser cảnh báo.
Nó không nhất thiết nói:
“Connection này không được mã hóa.”
Nó đang nói:
“Tôi không thể xác minh bạn đang kết nối với ai.”
Đối với local development, có thể sử dụng mkcert:
mkcert -install
mkcert localhost 127.0.0.1 ::1
Nó tạo một CA ngay trên máy của bạn.
Sau đó:
- Cài CA vào system trust store.
- Dùng CA đó để tạo certificate.
- Browser tin certificate vì trust store tin CA.
Kết quả:
Certificate có chain hợp lệ và browser không cần cảnh báo.
5.2. Debug TLS bằng cách nghĩ theo Certificate Chain
Khi TLS bị lỗi, đừng nghĩ:
“TLS bị hỏng.”
Hãy nghĩ:
“Mắt xích nào trong quá trình validation đang bị hỏng?”
Có thể kiểm tra theo thứ tự:
Domain / SAN
|
Expiration
|
Intermediate
|
Certificate Chain
|
Signature
|
Trust Store
|
Key Usage
Ví dụ certificate dành cho:
api.example.com
nhưng request tới:
api.test.com
→ kiểm tra SAN.
Certificate đã quá hạn:
Not Before
Not After
→ kiểm tra validity.
Server chỉ gửi server certificate nhưng không gửi intermediate certificate cần thiết.
→ client có thể không xây dựng được chain đầy đủ.
Đây là một lỗi rất dễ bỏ sót trong production.
5.3. Kiểm tra Certificate từ Server
Trước khi kết thúc, hãy chạy một command trên domain của chính bạn:
openssl s_client -connect yourdomain.com:443 -showcerts </dev/null 2>/dev/null | grep -c "BEGIN CERTIFICATE"
Con số mà command đó trả về cho biết:
Server thực sự đang gửi bao nhiêu certificate.
Nếu kết quả là:
1
thì server chỉ đang gửi một certificate.
Trong nhiều trường hợp, điều này có nghĩa server đang gửi server certificate nhưng thiếu intermediate certificate.
Một số browser có thể tự tìm intermediate certificate bằng những cơ chế khác, vì vậy lỗi có thể không xuất hiện ngay.
Nhưng các client khác như:
- Mobile application.
- Non-browser client.
- Một số TLS implementation.
có thể gặp certificate error.
Đây là lý do việc kiểm tra certificate chain trực tiếp từ server rất hữu ích khi debug production.
Phần 6. Tổng kết
Lần tới khi bạn nhìn thấy chiếc ổ khóa HTTPS, hãy nhớ rằng nó chỉ là phần nổi của tảng băng.
Đằng sau nó là cả một quy trình:
Client Hello
|
v
Certificate
|
v
Validation
|
v
ECDHE
|
v
Shared Secret
|
v
HKDF
|
v
Session Keys
|
v
Finished
|
v
Encrypted HTTP
TLS đồng thời giải quyết ba vấn đề:
Confidentiality — Dữ liệu không bị đọc.
Integrity — Dữ liệu không bị sửa mà bạn không biết.
Authentication — Bạn biết mình đang nói chuyện với đúng server.
Certificate giúp xác định identity.
CA giúp browser có cơ sở để tin identity đó.
ECDHE giúp hai bên tạo shared secret mà không cần truyền secret qua network.
HKDF giúp hai bên tạo session keys.
Symmetric encryption bảo vệ dữ liệu thực tế.
Và nhờ ECDHE với ephemeral key, TLS có Forward Secrecy để bảo vệ các session cũ ngay cả khi private key của server bị lộ trong tương lai.
Khi có lỗi, thường không phải:
“TLS bị hỏng một cách bí ẩn.”
Mà chỉ có một mắt xích cụ thể trong toàn bộ chain không còn hợp lệ.
Có thể là:
SAN
|
Expiry
|
Intermediate
|
Trust
|
Chain
Khi đã hiểu được flow này, certificate error không còn là một thứ quá bí ẩn nữa.
Bạn có thể bắt đầu hỏi:
“Mắt xích nào đang bị hỏng?”
Và từ đó biết mình cần kiểm tra ở đâu.
Chiếc ổ khóa nhỏ trên browser mà chúng ta nhìn thấy từ đầu chỉ là phần bề mặt của tất cả những cơ chế này.
All rights reserved