SSH Key Authentication và Hardening Server
Hướng dẫn xác thực SSH bằng khoá: chọn thuật toán, tạo khoá, cấu hình client, hardening sshd, authorized_keys nâng cao, SSH certificate, chống brute force và chuẩn bị hậu lượng tử.
SSH là cánh cửa chính của gần như mọi server Linux, nhưng cách cấu hình SSH phổ biến trên Internet vẫn dừng ở mức của nhiều năm trước: tạo khoá, copy lên server, tắt mật khẩu. Trong khi đó, bản thân OpenSSH đã thay đổi khá nhiều — thuật toán yếu bị xoá, mặc định bảo mật được siết lại, và chữ ký hậu lượng tử đã bật sẵn từ bản 10.6.
Bài này đi từ việc chọn đúng loại khoá, tạo khoá, cấu hình cả hai phía client và server, cho tới những thứ thường bị bỏ qua: authorized_keys với ràng buộc, SSH certificate, chống brute force, vòng đời khoá và lộ trình hậu lượng tử.
Vì sao mật khẩu không còn là lựa chọn
Mật khẩu trên SSH có hai vấn đề cố hữu: nó có thể bị đoán, và nó đi qua một kênh mã hoá nhưng vẫn phụ thuộc vào độ mạnh của chính nó. Chỉ cần một botnet quét cổng 22 là bạn sẽ thấy hàng nghìn lần thử đăng nhập mỗi ngày.
Điểm dễ bị bỏ sót là PasswordAuthentication mặc định vẫn là yes trong sshd. Nghĩa là một server mới cài, dù đã thêm khoá, vẫn chấp nhận đăng nhập bằng mật khẩu nếu bạn không tắt hẳn. Xác thực bằng khoá đảo ngược mô hình: server giữ public key, private key không bao giờ rời khỏi máy bạn, và kẻ tấn công không có gì để đoán.
Những thay đổi quan trọng của OpenSSH gần đây
Trước khi cấu hình, cần biết phiên bản mình đang chạy vì hành vi mặc định đã đổi:
| Phiên bản | Thay đổi đáng chú ý |
|---|---|
| 10.0 (04/2025) | Xoá hoàn toàn thuật toán chữ ký DSA (ssh-dss) |
| 10.1 (10/2025) | Cảnh báo khi kết nối dùng key exchange không hậu lượng tử; thêm WarnWeakCrypto phía client |
| 10.3 (04/2026) | Certificate có principals rỗng không còn được hiểu là wildcard |
| 10.4 (07/2026) | Hỗ trợ thử nghiệm chữ ký lai hậu lượng tử mldsa44-ed25519 |
| 10.5 (08/2026) | restrict trong authorized_keys áp dụng đúng cho cả tunnel forwarding |
| 10.6 (10/2026) | Bật mặc định chữ ký lai ssh-mldsa44-ed25519; WarnWeakCrypto cho cả server; default KDF rounds tăng lên 32; default RSA tăng lên 3072 bit |
Kiểm tra phiên bản và danh sách thuật toán đang hỗ trợ:
| |
Chọn loại khoá phù hợp
| Loại khoá | Khi nào dùng | Ghi chú |
|---|---|---|
ed25519 | Mặc định cho hầu hết trường hợp | Nhanh, khoá nhỏ, không phụ thuộc NIST curve. Là lựa chọn đầu tiên |
ed25519-sk | Khi cần khoá gắn phần cứng | Private key nằm trong FIDO2 token, không thể trích xuất |
mldsa44-ed25519 | Khi muốn thử chữ ký hậu lượng tử | Mới và chưa phổ biến rộng; chỉ dùng khi cả hai phía đều hỗ trợ |
rsa | Chỉ khi bắt buộc tương thích | Phải dài tối thiểu 3072 bit; mặc định mới của ssh-keygen đã là 3072 |
ecdsa | Chấp nhận được nhưng không lý tưởng | Phụ thuộc NIST curve; không có lợi thế so với Ed25519 |
dsa | Không dùng | Đã bị xoá khỏi OpenSSH từ bản 10.0 |
Về phía hệ sinh thái, GitHub cũng siết lại: từ 14/10/2026, mọi khoá RSA mới upload phải đạt tối thiểu 3072 bit, ssh-rsa dùng SHA-1 sẽ bị loại bỏ hoàn toàn vào 13/01/2027 (có brownout trước đó). Nếu bạn còn khoá RSA cũ trong CI/CD, đây là thứ cần xử lý ngay.
Tạo khoá đúng chuẩn
Tạo khoá Ed25519 với số vòng KDF cao hơn mặc định để chống brute-force khi private key bị lộ:
| |
-a 100: số vòng KDF (bcrypt_pbkdf). Mặc định hiện tại là 32; tăng lên giúp mỗi lần thử passphrase chậm hơn đáng kể với kẻ tấn công.-C: comment đặt theouser@máyđể sau này biết khoá nào của ai. Comment không ảnh hưởng bảo mật nhưng ảnh hưởng khả năng vận hành.- Luôn đặt passphrase. Private key không passphrase nghĩa là bất kỳ ai copy được file là đăng nhập được.
Đối chiếu fingerprint của khoá để dùng về sau khi xác minh host key:
| |
Nếu buộc phải dùng RSA, chỉ định rõ độ dài thay vì để mặc định:
| |
Khoá cứng FIDO2 cho môi trường nhạy cảm
Với server production hoặc tài khoản có quyền cao, khoá nằm trong thiết bị phần cứng là lựa chọn tốt hơn file trên ổ đĩa. Private key được sinh bên trong token và không thể trích xuất:
| |
-O resident: lưu key handle trong token, dùng được trên nhiều máy.-O verify-required: mỗi lần ký phải xác minh bằng PIN hoặc sinh trắc học.restrictvàverify-requiredcũng có thể ràng buộc ở phíaauthorized_keys.
Nạp khoá resident từ token vào agent:
| |
Đưa public key lên server
Cách gọn nhất là ssh-copy-id, nó thêm khoá vào đúng file với đúng quyền:
| |
Kiểm tra lại quyền trên server — sai quyền là nguyên nhân phổ biến nhất khiến publickey thất bại:
| |
sshd bật StrictModes yes mặc định: nếu thư mục home, ~/.ssh hoặc authorized_keys bị nhóm/người khác ghi được, sshd sẽ từ chối dùng khoá đó. Trên hệ thống có SELinux, nhớ gán lại nhãn:
| |
ssh-agent và cạm bẫy agent forwarding
Agent giữ private key đã giải mã trong bộ nhớ, giúp bạn không phải nhập passphrase liên tục:
| |
Trên macOS, thêm khoá vào Keychain bằng UseKeychain yes trong ~/.ssh/config để agent sống qua các lần reboot.
Agent forwarding (ForwardAgent) là thứ nên tránh mặc định. Khi bật, bất kỳ ai có quyền root trên máy đích đều có thể dùng agent socket được forward để ký bằng khoá của bạn — không lấy được private key, nhưng dùng được nó. Nếu buộc phải forward, hãy giới hạn theo từng host chứ đừng bật toàn cục. Từ OpenSSH 10.6 có AgentSocketPath cho phép kiểm soát vị trí socket agent trên server.
Cấu hình client gọn gàng và an toàn
Đặt mọi thứ vào ~/.ssh/config thay vì gõ cờ dài mỗi lần:
| |
IdentitiesOnly yes: chỉ dùng đúng khoá đã khai báo, tránh việc agent thử hàng loạt khoá và bị server đếm vàoMaxAuthTries.AddKeysToAgent confirm: hỏi trước khi nạp khoá vào agent, tránh việc khoá bị dùng ngầm.HashKnownHosts yes: che tên host trongknown_hostsphòng khi file bị lộ. Lưu ý mặc định vẫn làno.StrictHostKeyChecking accept-new: tự thêm host key mới nhưng vẫn từ chối khi host key thay đổi — điểm cân bằng tốt cho tự động hoá. Với môi trường yêu cầu kiểm soát chặt, dùngyesvà quản lýknown_hoststập trung.UpdateHostKeys yes: cho phép server gửi host key bổ sung sau khi xác thực, hỗ trợ xoay host key mượt mà.ServerAliveInterval: phát hiện kết nối chết, tránh treo phiên.
Mẹo kiểm tra khoá nào sẽ được thử, theo đúng thứ tự, mà không cần đăng nhập thật (từ 10.5):
| |
Xác minh host key và known_hosts
Client phải kiểm tra danh tính server, nếu không thì mã hoá mạnh đến đâu cũng vô nghĩa trước MITM. known_hosts hỗ trợ hai marker hữu ích:
@cert-authority: một dòng chứa khoá CA tin cậy cho cả một miền, ví dụ*.example.com.@revoked: đánh dấu khoá đã bị thu hồi, ssh sẽ luôn cảnh báo và không chấp nhận.
| |
Với hạ tầng có DNS an toàn, có thể dùng SSHFP để xác minh qua DNS:
| |
Khi cần thu hồi khẩn cấp, dùng Key Revocation List (KRL) gắn vào sshd qua RevokedKeys, hoặc RevokedHostKeys phía client.
Hardening phía server
Nguyên tắc an toàn: không sửa trực tiếp file cấu hình chính. Trên Debian/Ubuntu, sshd_config thường đã có Include /etc/ssh/sshd_config.d/*.conf; hãy tạo một file drop-in riêng:
| |
Giải thích nhanh các dòng quan trọng:
| Directive | Ý nghĩa |
|---|---|
PasswordAuthentication no | Tắt hẳn đăng nhập bằng mật khẩu |
KbdInteractiveAuthentication no | Tắt kênh keyboard-interactive (mặc định đang là yes) |
PermitRootLogin prohibit-password | Root không đăng nhập bằng mật khẩu; mặc định an toàn nhưng nên khai báo rõ |
AllowGroups ssh-users | Chỉ nhóm được chỉ định mới đăng nhập được |
MaxAuthTries 3 | Giảm số lần thử trước khi ngắt kết nối |
LoginGraceTime 30 | Rút ngắn cửa sổ cho kết nối chưa xác thực |
AllowTcpForwarding no | Tắt forwarding nếu không cần |
Kiểm tra cấu hình trước khi nạp lại — bước này bắt buộc:
| |
sshd -T in ra cấu hình hiệu lực sau khi áp dụng mọi drop-in và Match, đây là cách duy nhất chắc chắn biết mình đã tắt đúng thứ cần tắt. Trên các distro có systemd socket activation, hãy dùng systemctl reload ssh.socket nếu service được kích hoạt theo socket.
Chống tự khoá mình ra ngoài: luôn giữ một phiên SSH đang mở trong lúc thay đổi, và mở terminal thứ hai để kiểm tra đăng nhập trước khi đóng phiên cũ. Với người dùng cần ngoại lệ, dùng Match:
| |
authorized_keys không chỉ là danh sách khoá
Mỗi dòng trong authorized_keys có thể mang theo ràng buộc. Đây là công cụ bị dùng ít nhất so với giá trị của nó:
| Option | Tác dụng |
|---|---|
restrict | Tắt toàn bộ forwarding, PTY và ~/.ssh/rc; bật lại từng thứ khi cần |
from="..." | Chỉ chấp nhận từ dải IP/CIDR hoặc hostname nhất định |
command="..." | Ép chạy đúng một lệnh, bỏ qua lệnh client gửi |
expiry-time="..." | Khoá tự hết hạn theo ngày/giờ |
permitopen / permitlisten | Giới hạn đích của -L / -R forwarding |
no-touch-required / verify-required | Ràng buộc riêng cho khoá FIDO2 |
Ví dụ một khoá dành riêng cho job backup, chỉ chạy từ dải IP nội bộ và buộc đúng script:
| |
Lưu ý: force-command chỉ ép lệnh, không tự động tắt forwarding. Vì vậy restrict nên là điểm bắt đầu cho mọi khoá không cần shell tương tác.
SSH Certificate cho hạ tầng nhiều máy
Quản lý authorized_keys thủ công không mở rộng được. SSH certificate giải quyết bằng cách để mọi server chỉ tin một CA, còn user key được CA ký với thời hạn ngắn:
| |
Phía server, chỉ cần tin CA và quy định principal hợp lệ:
| |
CA cũng có thể được tin trực tiếp trong authorized_keys bằng cert-authority,principals="deploy". Ưu điểm lớn nhất là certificate hết hạn tự động, nên không còn tình trạng khoá cũ nằm lại trên hàng trăm server sau khi nhân sự thay đổi. Host certificate (ssh-keygen -h) áp dụng cùng ý tưởng cho host key, giúp xoay host key ở quy mô lớn.
Chống brute force và bật 2FA
Nếu từng dựng fail2ban chỉ để chặn brute force SSH, có tin tốt: sshd hiện đã có cơ chế phạt theo nguồn, bật mặc định qua PerSourcePenalties:
| Keyword | Mặc định | Ý nghĩa |
|---|---|---|
authfail | 5s | Xác thực thất bại rồi ngắt kết nối |
invaliduser | 5s | Thử user không tồn tại |
noauth | 1s | Kết nối rồi ngắt mà không xác thực |
grace-exceeded | 10s | Quá hạn LoginGraceTime |
max | 10m | Mức phạt tối đa tích luỹ |
Có thể siết thêm bằng PerSourceMaxStartups giới hạn số kết nối chưa xác thực từ một IP. fail2ban vẫn hữu ích khi cần ghi log tập trung hoặc chặn ở tầng firewall, nhưng nó không còn là lớp phòng thủ duy nhất.
Với tài khoản quyền cao, bật hai lớp xác thực bằng PAM (TOTP):
| |
Lưu ý AuthenticationMethods yêu cầu từng phương thức phải được bật tương ứng — nếu bạn đã tắt KbdInteractiveAuthentication, hãy bật lại cho nhóm user cần 2FA thay vì bật toàn cục.
Chuẩn bị cho hậu lượng tử
Traffic SSH hôm nay có thể bị ghi lại và giải mã sau này. OpenSSH đã chuyển sang key exchange lai hậu lượng tử mặc định:
mlkem768x25519-sha256nằm trong danh sáchKexAlgorithmsmặc định.- Từ 10.6, chữ ký lai
ssh-mldsa44-ed25519được bật mặc định; khoá tạo bằng bản thử nghiệm 10.4 phải tạo lại vì tên thuật toán đã thay đổi. WarnWeakCrypto(mặc địnhyes) sẽ ghi log khi một kết nối dùng key exchange không hậu lượng tử, giúp bạn phát hiện client cũ.
Hai điểm cần lưu ý khi vận hành:
| |
Nếu buộc phải tương thích với client cũ, dùng + để thêm chứ đừng thay thế danh sách mặc định. Về nén dữ liệu: từ 10.6 OpenSSH đã tắt bộ mã hoá từ điển LZ77 để vá một kênh side-channel trong phiên multiplex, nên Compression kém hiệu quả hơn; nếu cần nén, nén ở tầng ứng dụng sẽ tốt hơn.
Vòng đời khoá: xoay, thu hồi và sao lưu
Khoá không phải thứ tạo một lần rồi để đó. Một quy trình tối thiểu:
- Kiểm kê:
sshd -Tđể biết cấu hình hiệu lực,lastlog/journalctl -u sshđể xem ai đăng nhập bằng gì. - Xoay khoá: tạo khoá mới, thêm vào trước, xác minh đăng nhập được, rồi mới xoá khoá cũ. Không xoá trước khi thêm.
- Thu hồi: xoá dòng tương ứng trong
authorized_keys, hoặc dùng KRL khi số lượng lớn; với SSH certificate chỉ cần ngừng ký hoặc đưa vào KRL. - Sao lưu: backup private key ở nơi mã hoá, tách khỏi máy trạm. Mất private key không có cách nào phục hồi ngoài việc tạo lại.
- Rà soát khoá cũ: mọi khoá
ssh-rsakhông dùng SHA-2 hoặc nhỏ hơn 3072 bit nên được thay thế.
Ghi chú triển khai
- Thứ tự triển khai an toàn: thêm khoá → xác minh đăng nhập bằng khoá → mới tắt mật khẩu. Không làm ngược.
- Luôn có đường thoát: giữ một phiên SSH đang mở, và nếu có thể, một kênh console/IPMI trước khi động vào
sshd_config. - Kiểm tra bằng
sshd -trồisshd -Ttrước khi reload; drop-in trongsshd_config.dchỉ hoạt động nếu filesshd_configgốc có directiveInclude. - Không coi đổi cổng SSH là bảo mật. Nó chỉ giảm tiếng ồn từ scanner; tắt mật khẩu và hardening mới là biện pháp thật.
- Với tự động hoá/CI: dùng khoá riêng, ràng buộc bằng
restrict,from=, và tuyệt đối không nhúng private key không passphrase vào biến môi trường dùng chung. - Đừng copy
authorized_keysgiữa các máy khi có thể dùng SSH certificate; mỗi bản copy là một chỗ để quên thu hồi. - Trên môi trường dùng SELinux, sau khi tạo
~/.sshhoặc khôi phục từ backup, luôn chạyrestorecon -R -v ~/.ssh.
Troubleshooting thường gặp
Permission denied (publickey) — chạy ssh -vvv để xem client có gửi khoá hay không, sau đó kiểm tra phía server:
| |
Phần lớn trường hợp đến từ: sai quyền file, home bị group-writable, khoá không nằm trong AuthorizedKeysFile, hoặc SELinux context sai.
REMOTE HOST IDENTIFICATION HAS CHANGED — host key server đã đổi (cài lại, hoặc tấn công MITM). Xác minh fingerprint với quản trị viên qua kênh khác, rồi:
| |
sshd -t báo lỗi sau khi thêm drop-in — thường do option không được phép trong Match, hoặc sai tên directive. sshd -T với -C mô phỏng kết nối cụ thể sẽ cho biết cấu hình nào thực sự được áp dụng:
| |
Lời kết
SSH an toàn không nằm ở một dòng cấu hình thần kỳ, mà ở việc chọn đúng loại khoá, tắt hẳn mật khẩu, ràng buộc khoá bằng authorized_keys, và chuyển sang certificate khi số máy bắt đầu tăng. Phần hậu lượng tử đã được OpenSSH xử lý sẵn ở tầng key exchange — việc của bạn là đừng vô hiệu hoá nó vì một client cũ.
Hãy bắt đầu bằng việc kiểm tra server hiện tại: sshd -T xem PasswordAuthentication và PermitRootLogin đang là gì. Nếu vẫn là yes, đó là việc nên làm trước cả khi đọc hết bài này.
