[Hành trình tự học DevOps] Bài 3. SSH hoạt động như thế nào?
Bài viết được biên soạn dựa trên nội dung video How SSH Logs You In Without Ever Sending Your Password của Broken Patterns, 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.
🧑💻 Từ dòng lệnh đến một Secure Connection
Mỗi ngày, hàng triệu developer gõ gần như cùng một thứ:
ssh alice@server
Chỉ một giây sau, họ đã có mặt trong một terminal trên một cỗ máy ở cách xa nửa vòng Trái Đất — một cỗ máy mà họ không thể chạm tới về mặt vật lý, nhưng vẫn có thể điều khiển thông qua chính Internet công cộng mà tất cả mọi người đang sử dụng.
Và rất nhiều người có cùng một hình dung về những gì vừa xảy ra:
Password hoặc private key của họ đã được gửi qua mạng tới server, server kiểm tra nó, rồi cho phép họ đăng nhập.
Nghe có vẻ hợp lý.
Nhưng nếu bạn đăng nhập bằng SSH key, thì cách giải thích đó hoàn toàn sai.
Private key của bạn không rời khỏi máy tính. Không có private key nào được gửi tới server.
Khi hiểu được điều này, SSH không còn giống một chuỗi lệnh bí ẩn mà bạn chỉ học thuộc nữa. Nó trở thành một trong những ý tưởng rất đẹp của lĩnh vực máy tính:
Bạn có thể chứng minh mình sở hữu một bí mật mà không cần tiết lộ bí mật đó.
Nhưng để hiểu tại sao SSH có thể làm được điều này, chúng ta phải quay lại vấn đề mà nó được tạo ra để giải quyết.
Phần 1. SSH ra đời để giải quyết vấn đề gì?
Vào đầu những năm 1990, nếu muốn đăng nhập vào một máy tính từ xa, một trong những công cụ bạn có thể sử dụng là Telnet.
Vấn đề nghiêm trọng của Telnet là dữ liệu được truyền dưới dạng plaintext.
Username, password, câu lệnh bạn gõ...
Tất cả đều có thể đi qua mạng dưới dạng dữ liệu mà kẻ nghe lén có khả năng đọc được.
Hãy tưởng tượng đường truyền của bạn đi qua nhiều máy trung gian:
Client
|
v
Router
|
v
Internet
|
v
Server
Nếu một kẻ tấn công có khả năng quan sát lưu lượng ở đâu đó trên đường đi, dữ liệu Telnet có thể bị đọc.
Đây chính là vấn đề mà SSH ra đời để giải quyết.
Năm 1995, nhà nghiên cứu Tatu Ylönen phát triển SSH sau khi chứng kiến một cuộc tấn công đánh cắp password trên mạng của trường đại học nơi ông làm việc.
Mục tiêu của SSH là xây dựng một kết nối đáp ứng những yêu cầu quan trọng:
- Kẻ tấn công không thể đọc được dữ liệu.
- Bạn biết mình đang kết nối đúng Server.
- Server biết bạn là User nào.
Có thể tóm tắt thành ba mục tiêu:
- Bảo vệ dữ liệu
- Xác thực Server
- Xác thực User
Nhưng SSH không giải quyết tất cả những việc này bằng một loại key duy nhất.
Trong quá trình thiết lập connection, chúng ta sẽ gặp ba loại key quan trọng:
Session Keys
Host Key
User Key
Điều thú vị là mỗi loại key có một vai trò hoàn toàn khác nhau.
Phần 2. SSH Connection
Hãy bắt đầu từ thời điểm bạn chạy:
ssh alice@server
Client và Server sẽ lần lượt thực hiện một số bước để thiết lập connection.
Negotiation
|
Key Exchange
|
Server Auth
|
NEWKEYS
|
User Auth
2.1. Negotiation — Thống nhất "luật chơi"
Đây là bước đầu tiên.
Client và Server cần thống nhất:
"Chúng ta sẽ sử dụng những thuật toán nào?"
Hai bên gửi thông tin về các thuật toán mà mình hỗ trợ.
Ví dụ:
Client Server
| "Tôi biết A, B, C" |
|------------------------>|
| |
| "Tôi biết B, C, D" |
|<------------------------|
| |
| Chọn chung: B, C |
Danh sách này có thể bao gồm:
| Thuật toán | Chức năng | Ví dụ |
|---|---|---|
| Key Exchange | Tạo shared secret giữa Client và Server | curve25519-sha256 |
| Host Key Verification | Giúp Server chứng minh danh tính với Client | ssh-ed25519, rsa-sha2-512 |
| Encryption / Cipher | Mã hóa dữ liệu của SSH session | chacha20-poly1305@openssh.com, aes256-gcm@openssh.com |
| MAC | Kiểm tra toàn vẹn dữ liệu | hmac-sha2-256 |
| Compression | Nén dữ liệu trước khi truyền | none, zlib@openssh.com |
Client và Server sẽ chọn một thuật toán chung.
Ở bước này chưa có Session Keys.
Hai bên mới chỉ thống nhất:
"Chúng ta sẽ dùng cách nào để thiết lập connection."
2.2. Key Exchange — Tạo bí mật chung
Sau khi thống nhất thuật toán, Client và Server cần tạo ra một Shared Secret — một con số bí mật mà chỉ hai bên biết.
Họ dùng thuật toán Diffie-Hellman (thường là phiên bản Elliptic Curve).
Mỗi bên tạo một ephemeral key pair mới cho connection.
Private Key được sinh ngẫu nhiên, còn Public Key được tính từ Private Key và Generator Point G của curve — tham số đã được thống nhất từ bước Negotiation.
Client
|
+-- Private Key: a
+-- Public Key: A = a × G -> gửi Server
Server
|
+-- Private Key: b
+-- Public Key: B = b × G -> gửi Client
Sau đó hai bên trao đổi Public Key và tự mình tính ra Shared Secret:
Client tính: a × B = a × (b × G) = (a x b) x G
Server tính: b × A = b × (a × G) = (b x a) x G
Vì phép nhân trên curve có tính chất giao hoán, nên hai kết quả này hoàn toàn giống nhau:
(a × b) × G = (b × a) × G = Shared Secret
Đây là điểm mấu chốt của quá trình Key Exchange:
Hai bên tự tính ra được cùng một Shared Secret, dù không ai gửi private key của mình đi.
Người đứng giữa (nếu có nghe lén) chỉ thấy được G, A, B — không thể suy ngược ra a, b, hay Shared Secret nếu không giải được bài toán discrete logarithm trên đường cong elliptic.
Hãy lưu ý bộ key pair của Client và Server sinh ra ra quá trình key change chỉ là tạm thời (ephemeral) cho từng session. Nó mang lại một tính chất quan trọng: Forward Secrecy.
Điều này có nghĩa là nếu Private Key của Server bị lộ sau này, kẻ tấn công vẫn không thể giải mã các session trước đó, vì mỗi session đã sử dụng một ephemeral key pair riêng.
Từ Shared Secret đến Session Keys
Shared Secret chưa phải là key trực tiếp dùng để mã hóa toàn bộ traffic.
SSH sử dụng Shared Secret cùng với các thông tin của connection để tạo ra các Session Keys.
Có thể hình dung:
Shared Secret
|
v
Key Derivation
|
v
Session Keys
Vì vậy:
Shared Secret != Session Keys
Shared Secret là kết quả của Key Exchange.
Session Keys là các key được dẫn xuất để bảo vệ connection.
Tại sao cần nhiều Session Keys?
SSH không chỉ cần một key duy nhất.
Traffic có hai hướng:
Client -> Server
Server -> Client
Các key cho hai hướng được dẫn xuất riêng biệt.
Ngoài ra, tùy cipher/MAC được đàm phán, SSH có thể cần các key hoặc thông tin keying khác nhau cho mục đích bảo vệ dữ liệu và integrity.
Điểm quan trọng cần nhớ là:
Shared Secret chỉ là nguyên liệu, còn Session Keys mới là thứ được dùng trực tiếp cho quá trình mã hoá toàn bộ traffic.
2.3. Server Authentication — Server là ai?
Key Exchange giúp Client và Server tạo ra Shared Secret.
Nhưng Client vẫn cần biết:
"Server này có đúng là Server mình muốn kết nối không?"
SSH giải quyết việc này bằng Host Key.
Server có một key pair lâu dài:
Host Key Pair
|
+-- Host Public Key -> Client biết
+-- Host Private Key -> Server giữ
Trong quá trình Key Exchange, Server dùng Host Private Key để ký lên Exchange Hash, sau đó client dùng Host Public Key để verify Signature.
Signature = Sign(Exchange Hash, Host Private Key)
Valid = Verify(Signature, Exchange Hash, Host Public Key)
Exchange Hash là giá trị hash đại diện cho các thông tin quan trọng của phiên Key Exchange. RFC 5656 xác định chính xác các thành phần này.
Nếu quá trình validate hợp lệ, Client biết Server sở hữu Host Private Key tương ứng với Host Public Key mà nó tin tưởng.
Có một lưu ý nhỏ ở đây.
Lần đầu tiên khi bạn kết nối tới server:
ssh alice@server
Client chưa biết Host Public Key đó có thực sự thuộc về Server hay không.
Vì vậy SSH thường hiển thị cảnh báo kiểu:
The authenticity of host can't be established.
Nếu bạn chấp nhận, Client sẽ lưu key này vào:
~/.ssh/known_hosts
Những lần sau, SSH sẽ so sánh. Nếu key đột nhiên thay đổi — có thể là tấn công Man-in-the-Middle.
Đây là cơ chế "tin tưởng ngay lần đầu" (Trust On First Use — TOFU).
Ngắn gọn:
Client sử dụng Host Public Key để xác minh danh tính của Server.
2.4. NEWKEYS — Bắt đầu mã hóa
Ở bước Key Exchange, Client và Server đã tạo ra các Session Keys.
Nhưng việc có Session Keys và bắt đầu sử dụng chúng là hai chuyện khác nhau.
SSH sử dụng message:
SSH_MSG_NEWKEYS
để báo hiệu:
"Từ bây giờ, hãy chuyển sang sử dụng các key mới vừa tạo."
NEWKEYS không tạo ra key.
Nó cũng không phải bước authentication.
Nó là điểm chuyển sang sử dụng các key và thuật toán mới đã được thiết lập.
Đây là lúc SSH bắt đầu sử dụng symmetric cryptography để bảo vệ traffic thực tế từ các Session Keys đã tạo.
Ví dụ, SSH có thể sử dụng các cipher hiện đại như AES hoặc ChaCha20 tùy thuật toán được đàm phán. Có thể hình dung:
- Mã hoá:
Ciphertext = Encrypt(Plaintext, Session Key)
- Giải mã:
Plaintext = Decrypt(Ciphertext, Session Key)
Symmetric cryptography phù hợp cho việc này vì nó nhanh hơn nhiều so với việc dùng public-key cryptography để mã hóa toàn bộ traffic.
Ngắn gọn:
NEWKEYS thông báo với Client và Server rằng, từ nay mọi traffic sẽ được mã hoá bằng Session Keys.
2.5. User Authentication — Bạn là ai?
Bây giờ Transport Layer đã được thiết lập và traffic đã được bảo vệ.
Tiếp theo Server cần biết:
"Bạn là User nào?"
Đây là User Authentication.
Có nhiều phương thức authentication, trong đó phổ biến nhất là:
- Password
- Public Key
Xác thực bằng Password
Nếu User nhập:
password123
password không được gửi trực tiếp dưới dạng plaintext trên network.
Nó được truyền bên trong SSH connection đã được bảo vệ bởi Session Keys.
Vì vậy, attacker đang nghe traffic trên network không đơn giản có thể đọc được password.
Xác thực bằng User Public Key
Phương pháp này được sử dụng phổ biến hơn xác thực bằng Password.
Nó dựa trên cùng nguyên lý xác thực Server được đề cập trong mục 2.3, nhưng lần này vai trò được đổi lại:
Server dùng User Public Key để xác minh danh tính của User.
Cụ thể, Client có một key pair lâu dài:
User Key Pair
|
+-- User Public Key -> Server biết
+-- User Private Key -> User giữ
User Private Key nằm trên Client, còn User Public Key được đặt trên Server, thường trong:
~/.ssh/authorized_keys
Khi bạn đăng nhập, Client sử dụng User Private Key để tạo chữ ký trên dữ liệu của phiên SSH, sau đó Server sử dụng User Public Key đã lưu trước đó tại ~/.ssh/authorized_keys để kiểm tra:
Signature = Sign(Data, User Private Key)
Valid = Verify(Data, Signature, User Public Key)
Data ở đây là dữ liệu của request xác thực, bao gồm Session ID và các thông tin xác thực như username, service và public key. RFC 4252 quy định rõ cấu trúc này.
Nếu chữ ký hợp lệ, Server có thể kết luận:
Danh tính của user được xác nhận vì nó sở hữu User Private Key tương ứng với User Public Key đã được cấu hình trên Server.
Đến đây, quá trình thiết lập SSH đã hoàn tất. Server và User đã xác thực lẫn nhau, Session Keys đã được thiết lập, và từ thời điểm này, hai bên có thể an toàn trao đổi dữ liệu qua SSH connection.
Phần 3. Bên trong thư mục .ssh
Bây giờ chúng ta đã hiểu protocol hoạt động như thế nào. Tiếp theo, hãy xem những khái niệm đó xuất hiện ở đâu trên filesystem.
Trên Client (máy user):
~/.ssh/
|
+-- id_ed25519 -> User Private Key
+-- id_ed25519.pub -> User Public Key
+-- known_hosts -> Danh sách Host Public Key của các server đã kết nối
Trên Server:
/etc/ssh/
|
+-- ssh_host_ed25519_key -> Host Private Key
+-- ssh_host_ed25519_key.pub -> Host Public Key
và
~/.ssh/authorized_keys -> Danh sách User Public Key được phép đăng nhập
Host Key được lưu trong /etc/ssh/ thay vì ~/.ssh/ vì nó đại diện cho cả Server, không phải cho một user cụ thể.
Đây cũng là lý do User Private Key và Host Private Key là hai khái niệm hoàn toàn khác nhau.
Nhắc lại một lần nữa:
| File | Vai trò | Lưu tại |
|---|---|---|
id_ed25519 |
User Private Key — chứng minh User | Client |
authorized_keys |
User Public Key — kiểm tra User | Server |
ssh_host_*_key |
Host Private Key — chứng minh Server | Server |
known_hosts |
Host Public Key — nhận diện Server | Client |
Phần 4. SSH không chỉ để mở shell
Sau khi Transport Layer và User Authentication hoàn tất, SSH không chỉ đơn giản là "mở một terminal".
SSH có một khái niệm quan trọng gọi là Channel.
Một SSH connection có thể chứa nhiều Channel.
Ví dụ:
SSH Connection
|
+-- Shell
+-- Command
+-- Port Forwarding
Bạn có thể mở interactive shell, chạy trực tiếp một command hoặc sử dụng port forwarding:
ssh alice@server # Mở shell tương tác
ssh alice@server "ls -la" # Chạy lệnh rồi thoát
ssh -L 5432:localhost:5432 alice@server # Chuyển tiếp cổng
Các hoạt động này có thể sử dụng các Channel khác nhau nhưng vẫn nằm trên cùng một SSH connection. Có thể hình dung:
SSH Connection
|
+-- Channel 1: Shell
+-- Channel 2: Command
+-- Channel 3: Port Forwarding
Điều quan trọng:
SSH Connection là đường hầm, còn Channel là các luồng sử dụng đường hầm đó.
Phần 5. Tổng kết
Lần tới khi bạn gõ:
ssh alice@server
hãy hình dung rằng phía sau một dòng lệnh đơn giản đó là cả một quá trình.
SSH là một chuỗi cơ chế mật mã được kết hợp khéo léo:
- Server chứng minh danh tính bằng Host Key.
- Bạn chứng minh danh tính bằng User Key — mà không gửi private key đi đâu.
- Dữ liệu được bảo vệ bằng Session Keys sinh ra từ quá trình trao đổi bí mật.
Và ý tưởng cốt lõi vẫn là một câu:
Bạn có thể chứng minh mình sở hữu một bí mật mà không cần gửi bí mật đó cho người khác.
SSH không phải phép thuật.
Nó chỉ là cách an toàn để nói chuyện với một máy tính mà bạn không thể chạm tới.
All rights reserved