Ok, mình sẽ đi theo đúng trình tự handshake của SSH theo RFC (chủ yếu là RFC 4253 – The Secure Shell (SSH) Transport Layer Protocol).

Mình sẽ giải thích theo flow thực tế khi bạn gõ:

ssh user@vps

Và phân tách rõ:

  • Transport Layer (RFC 4253)
  • User Authentication (RFC 4252)
  • Connection Protocol (RFC 4254)

1. Tổng quan kiến trúc SSH theo RFC

SSH được chia thành 3 lớp:

  1. Transport Layer Protocol (RFC 4253)
  2. User Authentication Protocol (RFC 4252)
  3. Connection Protocol (RFC 4254)

Handshake chủ yếu xảy ra ở Transport Layer.


2. Bước 1 – Version Exchange

RFC 4253 section 4.2

Ngay sau khi TCP kết nối tới port 22:

Client gửi:

SSH-2.0-OpenSSH_9.6

Server gửi:

SSH-2.0-OpenSSH_8.9

Cả hai bên:

  • Kiểm tra version
  • Nếu không hỗ trợ SSH-2 → đóng kết nối

Tại thời điểm này: chưa mã hóa.


3. Bước 2 – Key Exchange Init (KEXINIT)

RFC 4253 section 7.1

Hai bên gửi gói:

SSH_MSG_KEXINIT

Trong đó chứa:

  • danh sách thuật toán key exchange
  • danh sách host key algorithm
  • danh sách cipher
  • MAC algorithm
  • compression

Ví dụ client gửi danh sách:

kex_algorithms:
  curve25519-sha256
  diffie-hellman-group14-sha256server_host_key_algorithms:
  ssh-ed25519
  rsa-sha2-512ciphers:
  aes128-ctr
  [email protected]

Server cũng gửi tương tự.

Hai bên chọn:

  • thuật toán đầu tiên trùng nhau trong danh sách

Đây gọi là algorithm negotiation.


4. Bước 3 – Diffie-Hellman Key Exchange

RFC 4253 section 8

Giả sử dùng:

curve25519-sha256

Flow sẽ như sau:

4.1 Client gửi

SSH_MSG_KEX_ECDH_INIT

chứa:

e = client_ephemeral_public_key

4.2 Server phản hồi

SSH_MSG_KEX_ECDH_REPLY

chứa:

  • server host public key
  • f = server ephemeral public key
  • signature

5. Tính Shared Secret

Client có:

  • private ephemeral key (x)
  • server public ephemeral key (f)

Server có:

  • private ephemeral key (y)
  • client public ephemeral key (e)

Cả hai tính:

K = ECDH(x, f)
K = ECDH(y, e)

Kết quả giống nhau.

Đây là shared secret.


6. Exchange Hash H

RFC 4253 section 8

Cả hai tạo:

H = HASH(
  client_version_string ||
  server_version_string ||
  client_kexinit_payload ||
  server_kexinit_payload ||
  server_host_key ||
  e ||
  f ||
  K
)

H rất quan trọng.

Nó được dùng để:

  • xác thực server
  • tạo session keys

7. Server ký Exchange Hash

Server dùng private host key để ký H.

Ví dụ nếu dùng ed25519:

signature = sign(H, server_host_private_key)

Server gửi signature trong SSH_MSG_KEX_ECDH_REPLY.

Client:

  • lấy public host key
  • verify(signature, H)

Nếu verify thất bại → MITM attack → đóng kết nối.


8. Tạo session keys

RFC 4253 section 7.2

Sau khi có:

  • K (shared secret)
  • H (exchange hash)

SSH tạo:

  • encryption key
  • MAC key
  • IV

bằng cách hash:

key = HASH(K || H || letter || session_id)

letter là A, B, C, D để sinh các key khác nhau.

session_id = H của lần handshake đầu tiên.


9. Chuyển sang mã hóa

Client gửi:

SSH_MSG_NEWKEYS

Server gửi lại:

SSH_MSG_NEWKEYS

Từ thời điểm này:

Toàn bộ traffic được mã hóa.


10. User Authentication (RFC 4252)

Sau khi transport layer hoàn tất.

Client gửi:

SSH_MSG_SERVICE_REQUEST

request:

ssh-userauth

Server chấp nhận.

Sau đó bắt đầu authentication.

Ví dụ public key auth:

Client gửi:

SSH_MSG_USERAUTH_REQUEST

kèm:

  • username
  • service
  • method = publickey
  • public key
  • signature

Signature ở đây ký:

session_id || SSH_MSG_USERAUTH_REQUEST fields

Server verify bằng public key trong authorized_keys.


11. Sau khi auth thành công

Server gửi:

SSH_MSG_USERAUTH_SUCCESS

Bắt đầu Connection Protocol (RFC 4254).

Client mở channel:

SSH_MSG_CHANNEL_OPEN

ví dụ:

  • session
  • port forwarding
  • exec command

12. Bảo mật chống MITM nằm ở đâu

Quan trọng nhất là:

  • server ký H bằng host private key
  • client đã lưu known_hosts

Nếu attacker chèn giữa:

  • không có private host key
  • không ký đúng H
  • client phát hiện

13. Toàn bộ handshake tóm tắt

  1. TCP connect
  2. Version exchange
  3. KEXINIT
  4. Diffie-Hellman
  5. Server ký exchange hash
  6. Tạo session keys
  7. NEWKEYS
  8. User Authentication

14. Điểm nâng cao mà nhiều người không để ý

  • session_id không đổi trong suốt session
  • rekey có thể xảy ra định kỳ
  • forward secrecy đến từ ephemeral Diffie-Hellman
  • host key chỉ dùng để ký, không dùng để mã hóa session