SSH là gì và hoạt động như thế nào?
Tìm hiểu SSH mã hóa kết nối đến server từ xa ra sao, mô hình client-server, port 22, host key fingerprint và khác biệt giữa đăng nhập bằng key với password.
SSH là gì?
SSH (secure shell) là một protocol dùng để đăng nhập vào một máy tính từ xa và chạy lệnh trên máy đó qua một kết nối được mã hóa. Nội dung bạn nhập được gửi đến máy từ xa, output của máy được gửi ngược lại, và người theo dõi network ở giữa không thể đọc được cả hai chiều. Linux server thuê ngoài không có màn hình hoặc bàn phím gắn trực tiếp, nên SSH là cách để sử dụng máy đó.
Tên gọi này bao gồm hai khái niệm. SSH là protocol, được mô tả trong RFC 4251 đến RFC 4254. OpenSSH là chương trình triển khai protocol này, và gần như mọi Linux server cũng như laptop hiện nay đều chạy nó. Khi nói "SSH vào server", nghĩa là client program ssh trên máy của bạn đang giao tiếp với server program sshd ở đầu bên kia.
Vấn đề mà SSH được tạo ra để thay thế
Đăng nhập từ xa có từ rất lâu trước SSH. Telnet mở một kết nối TCP không mã hóa đến cổng 23 và gửi từng byte đúng như bạn đã nhập. Không có dữ liệu nào được mã hóa, kể cả password. Bất kỳ ai có thể xem network traffic đều đọc được nội dung đó: một người trên cùng mạng văn phòng hoặc operator của bất kỳ router nào trên đường truyền. Họ rlogin cũng có điểm yếu tương tự. Nó tin cậy máy client dựa trên tên máy, tức là tin vào bất cứ thông tin nào mà network khai báo cho tên đó.
Tatu Ylönen viết SSH đầu tiên vào năm 1995 tại Helsinki University of Technology, sau một cuộc tấn công sniffing password trên network của trường. Thiết kế này giữ lại phần hữu ích của Telnet, tức một byte stream giữa terminal của bạn và remote shell, đồng thời bổ sung hai điều mà Telnet không giải quyết được: mã hóa stream và bằng chứng cho thấy server ở đầu bên kia đúng là server bạn muốn kết nối.
Phần thứ hai này rất dễ bị bỏ qua, nhưng nó chiếm một nửa vai trò của SSH. Chỉ mã hóa thôi thì không bảo vệ được bạn. Một machine ở giữa có thể nhận kết nối của bạn, mã hóa hoàn toàn chính xác, đọc mọi thứ bạn gửi rồi chuyển tiếp đến server thật. SSH ngăn việc đó bằng cách cấp cho mỗi server một danh tính cố định gọi là host key và kiểm tra danh tính này trong từng kết nối.
Cách mô hình client và server hoạt động
Có hai chương trình. Trên server, sshd luôn chạy và chờ kết nối. Trên máy của bạn, ssh tạo các kết nối đó. Đây là hai chương trình riêng với các file cấu hình riêng. Nhầm lẫn giữa hai chương trình là lý do phổ biến nhất khiến một thay đổi không có hiệu lực.
- Server đọc
/etc/ssh/sshd_config. Đây là nơi tắt đăng nhập bằng password và đặt listening port. - Client đọc
/etc/ssh/ssh_configcho các thiết lập mặc định của hệ thống, sau đó đọc~/.ssh/configcho các thiết lập riêng theo từng host của bạn.
Trên Debian và Ubuntu, service unit có tên ssh. Trên RHEL, Rocky và Fedora, service unit có tên sshd. Các bản Ubuntu gần đây cài đặt service này ở chế độ socket activated, nên systemctl status ssh có thể báo inactive (dead) trong khi máy vẫn truy cập được bình thường, vì ssh.socket là unit đảm nhiệm việc lắng nghe và khởi động service theo yêu cầu.
Client không nhất thiết phải là OpenSSH. PuTTY trên Windows, Termius trên điện thoại và tính năng remote support tích hợp trong các editor đều dùng cùng một protocol để kết nối đến cùng sshd. Windows 10 và 11 cũng tích hợp OpenSSH client, nên ssh you@server hoạt động trong PowerShell mà không cần cài thêm gì.
Vì sao SSH sử dụng cổng 22?
Cổng là một số cho kernel biết kết nối đến thuộc về chương trình nào đang lắng nghe, và các cổng trên Linux hoạt động giống nhau đối với mọi service. SSH sử dụng cổng 22 vì IANA đã gán cổng này vào năm 1995. Ylönen yêu cầu một số cổng còn trống, nằm cạnh các giao thức mà SSH được viết để thay thế: 21 là FTP, 23 là telnet, còn 22 chưa được sử dụng.
Vì 22 là cổng mặc định nên mọi thứ đều giả định cổng này. Git remote, script backup và control panel của nhà cung cấp đều thử cổng 22 trước. Mọi scanner tự động trên Internet cũng vậy. Một server mới bật đăng nhập bằng password sẽ bắt đầu ghi các dòng như sau vào /var/log/auth.log chỉ vài phút sau khi boot:
Failed password for invalid user admin from 203.0.113.55 port 43122 ssh2Lưu lượng này diễn ra liên tục và không nhắm riêng vào bạn. Đổi sshd sang cổng 2222 sẽ loại bỏ phần lớn các dòng đó, vì scanner đang quét toàn bộ Internet trên cổng 22 thay vì phân tích server của bạn. Việc này không khiến máy khó bị xâm nhập hơn đối với người thực sự kiểm tra nó. Hãy xem đổi cổng chỉ là cách giảm nhiễu, không hơn.
Bạn có thể xem server phản hồi trước khi đăng nhập:
nc 203.0.113.10 22Trên Ubuntu 24.04, lệnh này in ra nội dung gần giống SSH-2.0-OpenSSH_9.6p1 Ubuntu-3ubuntu13. Banner được gửi ở dạng plaintext, trước khi có mã hóa, vì hai phía cần dùng nó để thống nhất phiên bản giao thức. Nhấn Ctrl+C để đóng kết nối.
Điều gì xảy ra trên đường truyền khi bạn kết nối
Trình tự dưới đây là những gì một ssh you@server thực hiện trước khi bạn thấy prompt.
- Client phân giải hostname thành địa chỉ IP, rồi mở kết nối TCP đến port 22.
- Hai phía gửi version banner ở dạng cleartext.
- Hai phía gửi danh sách các algorithm mà chúng hỗ trợ: key exchange, cipher, xác thực message và compression. Dữ liệu vẫn ở dạng cleartext. Hai phía chọn tùy chọn mạnh nhất mà cả hai cùng hỗ trợ.
- Key exchange diễn ra. OpenSSH hiện tại ưu tiên
curve25519-sha256. Cuối cùng, hai đầu đều giữ cùng một shared secret mà secret đó chưa từng truyền qua network. Vì vậy, người ghi lại toàn bộ cuộc trao đổi cũng không thể tính ra secret này về sau. - Server ký kết quả của quá trình trao đổi bằng host private key. Client của bạn kiểm tra chữ ký dựa trên host public key đã lưu. Đây là bước ngăn một máy ở giữa giả mạo server của bạn.
- Mã hóa bắt đầu.
chacha20-poly1305@openssh.comlà cipher mặc định trong OpenSSH hiện tại. - Chỉ sau đó client mới xác thực bạn bằng password hoặc key. Username và password của bạn được truyền bên trong encrypted channel.
- Client mở một channel và yêu cầu shell.
Thứ tự trong danh sách này là điểm khác biệt hoàn toàn so với telnet. Xác thực diễn ra sau khi channel đã được mã hóa và sau khi server đã chứng minh danh tính. Vì vậy, password của bạn không có lúc nào được truyền công khai trên network.
Người theo dõi network vẫn biết được một số thông tin. Họ thấy địa chỉ IP của bạn, địa chỉ IP của server, port 22, hai version banner ở dạng cleartext, cùng thời điểm và kích thước tương đối của từng packet. Họ không thấy username, password, command hoặc output của bạn. Tra cứu hostname ở bước 1 không thuộc SSH và thường không private. Vì vậy, DNS query phân giải tên server của bạn có thể tiết lộ bạn sắp kết nối đến máy nào, dù session vẫn được bảo vệ.
Khóa host và prompt fingerprint khi kết nối lần đầu
Khi cài openssh-server, hệ thống tạo các cặp host key cho máy chủ và ghi chúng vào /etc/ssh/, chẳng hạn ssh_host_ed25519_key và ssh_host_ed25519_key.pub. Phần private không bao giờ rời khỏi server. Phần public là danh tính của server. Chữ ký ở bước 5 được kiểm tra dựa trên phần public này.
Lần đầu kết nối đến một server mới, client không có khóa nào để đối chiếu nên sẽ hỏi bạn:
The authenticity of host '203.0.113.10 (203.0.113.10)' can't be established.
ED25519 key fingerprint is SHA256:E9nVQ5Sm2oQ3nGm5Zf1tOaU7Xh0k2p8bWc4dLrTvYxA.
This key is not known by any other names.
Are you sure you want to continue connecting (yes/no/[fingerprint])?Fingerprint là giá trị băm SHA256 của host public key, được in ở dạng base64 nên đủ ngắn để đối chiếu bằng mắt. Nhập yes sẽ ghi khóa đó vào ~/.ssh/known_hosts trên máy của bạn. Trong mọi lần kết nối sau đến cùng địa chỉ, client sẽ so sánh khóa server cung cấp với khóa đã lưu. Khi hai khóa khớp nhau, client không in thông báo nào và bạn được đưa thẳng đến prompt.
Mô hình này được gọi là trust on first use. Bạn cần hiểu rõ cái giá của nó. Lần kết nối đầu tiên là thời điểm duy nhất bạn chưa được bảo vệ, vì bạn đang chấp nhận một khóa chưa từng thấy trước đó. Để loại bỏ rủi ro này, hãy lấy fingerprint bằng một kênh khác rồi đối chiếu. Hầu hết nhà cung cấp đều in fingerprint trong output boot hiển thị trên web console. Bạn cũng có thể in fingerprint ngay trên server:
ssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pubLệnh này in ra cùng chuỗi SHA256: mà prompt đã hiển thị. Lựa chọn [fingerprint] trong prompt tồn tại chính vì mục đích này: dán fingerprint bạn mong đợi vào đó. Client chỉ tiếp tục nếu fingerprint khớp với fingerprint server cung cấp.
Trên Debian và Ubuntu, known_hosts được hash theo mặc định. Vì vậy, file chứa các dòng bắt đầu bằng |1| thay vì hostname ở dạng dễ đọc. Chạy ssh-keygen -F 203.0.113.10 để tìm entry của một host.
Tại sao SSH báo host key đã thay đổi?
Sớm muộn gì bạn cũng sẽ gặp một khối văn bản dài như sau:
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!Thông báo kết thúc bằng Host key verification failed. và client từ chối kết nối. Nó cũng in Password authentication is disabled to avoid man-in-the-middle attacks., vì việc nhập password vào một máy không xác định chính là rủi ro mà cơ chế kiểm tra này được thiết kế để ngăn chặn.
Thông báo nghe như một sự cố khẩn cấp, nhưng phần lớn trường hợp không phải vậy. Các nguyên nhân thường gặp:
- Bạn đã rebuild hoặc cài lại server, nên
sshdtạo host key mới trong lần boot đầu tiên. Đây là lý do phổ biến nhất. - Bạn đã xóa một VPS rồi tạo VPS khác, và nhà cung cấp cấp IP cũ cho máy mới.
- Bạn đang kết nối qua một forward hoặc load balancer, hiện đã chuyển đến một backend khác.
- Có thứ gì đó thực sự đang intercept kết nối.
Hãy xác định nguyên nhân trước khi xóa bất kỳ thứ gì. Nếu bạn vừa cài lại máy cách đây mười phút thì nguyên nhân đã rõ. Nếu phía bạn không có thay đổi nào, hãy dừng lại và điều tra, vì warning này đang thực hiện đúng chức năng kiểm tra của nó. Khi đã chắc chắn, hãy xóa entry cũ rồi kết nối lại:
ssh-keygen -R 203.0.113.10Lần kết nối tiếp theo sẽ hiển thị lại prompt fingerprint. Bạn có thể dùng cơ hội này để đối chiếu fingerprint mới với provider console.
Đăng nhập bằng password hay bằng key
Xác thực bằng password gửi password của bạn trong kênh đã được mã hóa, rồi sshd kiểm tra password đó trong cơ sở dữ liệu tài khoản, thường thông qua PAM (pluggable authentication modules). Cách này không cần chuẩn bị trước. Vì vậy, nhà cung cấp có thể cấp cho bạn một server mới chỉ có root password.
Điểm yếu không nằm ở mã hóa. Vấn đề là password là một secret ngắn, bạn gửi nó đến server trong mỗi lần đăng nhập, và port 22 bị các máy tự động không bao giờ mệt mỏi dò đoán suốt ngày đêm.
Xác thực bằng public key hoạt động theo cách khác. Bạn tạo một cặp key trên máy của mình. Phần public được đặt vào ~/.ssh/authorized_keys trong tài khoản của bạn trên server. Phần private nằm trên laptop của bạn và không bao giờ được truyền đi. Khi đăng nhập, client ký một phần dữ liệu có chứa session identifier từ quá trình trao đổi key, rồi server xác minh chữ ký đó bằng public key mà server đã lưu. Vì dữ liệu được ký gắn với đúng session này, chữ ký bị thu thập sẽ không có giá trị đối với bất kỳ session nào khác.
Hãy chú ý hướng triển khai, vì việc đảo ngược rất dễ xảy ra và gây hại: public key đặt trên server, private key giữ ở phía bạn. Một private key bị sao chép lên server là một private key mà bạn không thể tiếp tục tin cậy.
Đăng nhập bằng key cũng có các nguyên nhân lỗi riêng. sshd sẽ bỏ qua các key khi quyền của file quá rộng, và log của server sẽ ghi rõ điều đó:
Authentication refused: bad ownership or modes for directory /home/ubuntu/.sshClient chỉ báo cho bạn Permission denied (publickey). Đây là cùng một thông báo cho hàng chục nguyên nhân khác nhau. Vì vậy, bạn nên học cách đọc đúng lỗi publickey trước khi bị khóa quyền truy cập. Các thao tác thực tế như tạo key, bảo vệ key bằng passphrase và nạp key vào agent được trình bày trong quản lý SSH key. Việc tắt đăng nhập bằng password mà không tự khóa quyền truy cập được trình bày trong tăng cường bảo mật SSH trên VPS.
SFTP, scp và port forwarding sử dụng cùng một kết nối
Đây là nguyên lý giúp các tính năng còn lại của SSH trở nên dễ hiểu. Việc xác thực mở một kết nối được mã hóa. Kết nối đó có thể đồng thời mang nhiều channel độc lập. Shell chỉ là một loại channel trong số đó.
- Remote shell.
ssh you@servermở một session channel và yêu cầu một interactive shell. - Một lệnh đơn.
ssh you@server uptimemở một channel, chạy một lệnh, in output rồi thoát. - SFTP. Client yêu cầu
sshdkhởi động subsystemsftp, rồi truyền file bên trong cùng kết nối. SFTP là giao thức truyền file chạy trên SSH và không dùng chung thiết kế với FTP. Giao thức FTP được bổ sung mã hóa có tên là FTPS và không liên quan đến SFTP. - scp. Sao chép file bằng cùng thông tin đăng nhập. Từ OpenSSH 9.0, được phát hành vào năm 2022,
scpmặc định sử dụng giao thức SFTP ở bên dưới. - Port forwarding.
ssh -L 8080:localhost:80 you@serverbiến port 8080 trên laptop thành đường chuyển tiếp đến port 80 trên server, chạy bên trong kết nối được mã hóa.-Rchuyển tiếp theo hướng ngược lại, còn-D 1080biến session thành SOCKS proxy. - Git. Một remote như
git@github.com:user/repo.gitlà một lần đăng nhập SSH mà phía remote chạy command handler thay vì shell. - rsync và Ansible cũng là SSH client. Chúng mở một channel, chạy một tác vụ rồi đọc output trả về.
Mọi mục trong danh sách đều dùng cùng port, cùng bước kiểm tra host key và cùng thông tin xác thực. Vì vậy, thiết lập key authentication một lần sẽ mang lại lợi ích ngay: mọi công cụ này đều dùng lại cấu hình đó. Đây cũng là lý do cùng một file ~/.ssh/config vừa rút ngắn thời gian đăng nhập, vừa hỗ trợ mở rộng khi bạn quản lý nhiều server Linux từ một laptop.
SSH không làm được gì
- SSH không làm máy chủ của bạn an toàn. SSH bảo vệ đường truyền đến cổng truy cập. Cổng đó vẫn tồn tại và người khác vẫn sẽ tiếp tục thử đăng nhập. Chặn các lần thử đăng nhập lặp lại bằng fail2ban xử lý số lượng yêu cầu, còn chỉ cho phép xác thực bằng key sẽ loại bỏ thứ mà họ đang cố đoán.
- SSH không bảo vệ bạn khỏi chính thiết bị của mình. Bất kỳ ai có quyền truy cập vào laptop của bạn đều có private key và agent đã được nạp key.
- SSH không che giấu việc bạn đang dùng SSH. Số cổng và version banner dạng cleartext đều cho biết điều đó.
- SSH không bảo vệ những gì xảy ra trước khi kết nối được thiết lập. Việc phân giải tên và quyết định của bạn về địa chỉ nào đáng tin cậy đều xảy ra trước.
Đi tiếp từ đây
Nếu hiện có một server mới mở trong console của nhà cung cấp, hãy làm theo đúng thứ tự sau. Đăng nhập, tạo user thường, cài cryptographic key của bạn, rồi đóng các đường truy cập dễ bị khai thác. Mười phút đầu tiên trên một VPS mới hướng dẫn toàn bộ quy trình theo thứ tự này, còn VPS thực sự là gì giải thích phần máy bên dưới nếu các thuật ngữ này vẫn còn mới. Sau đó, hãy đọc hai bài về key và hardening theo đúng thứ tự đó.
FAQ
SSH là viết tắt của gì?
SSH là viết tắt của secure shell. Đây là một giao thức để đăng nhập vào máy tính từ xa và chạy lệnh trên đó qua kết nối được mã hóa, được định nghĩa trong RFC 4251 đến RFC 4254. OpenSSH là implementation mà gần như mọi người đều dùng: client ssh trên máy của bạn và server sshd trên máy từ xa. SSH thay thế telnet, vốn gửi mọi thứ, kể cả mật khẩu, qua network dưới dạng văn bản thuần.
Vì sao SSH dùng cổng 22?
IANA đã gán cổng 22 cho SSH vào năm 1995, bên cạnh FTP ở cổng 21 và telnet ở cổng 23, là những protocol mà SSH được viết để thay thế. Không có gì bắt buộc phải dùng số này: Port trong /etc/ssh/sshd_config thay đổi cổng trên server, còn ssh -p chọn cổng khác trên client. Vì 22 là giá trị mặc định, các automated scanner liên tục quét cổng này. Vì vậy, /var/log/auth.log của một server mới thường đầy các dòng Failed password for invalid user. Đổi cổng chỉ giảm lượng log nhiễu và không tăng bảo vệ thực sự.
Tôi nên làm gì khi SSH cảnh báo host key đã thay đổi?
Hãy tìm nguyên nhân trước khi xóa bất kỳ thứ gì. Lý do thường gặp và không đáng lo là server đã được dựng lại, nên sshd tạo host key mới, hoặc một máy mới được gán địa chỉ IP cũ. Nếu bạn biết máy đã được dựng lại, chạy ssh-keygen -R <host> để xóa key đã lưu, kết nối lại, rồi so sánh fingerprint được hiển thị với fingerprint do console của provider cung cấp. Nếu phía bạn không có gì thay đổi, đừng kết nối và đừng nhập mật khẩu. OpenSSH vốn đã từ chối password authentication trong trạng thái này chính vì lý do đó.
SFTP và scp có khác SSH không?
Chúng chạy trên SSH. Sau khi bạn xác thực, kết nối SSH có thể mang nhiều channel, còn shell chỉ là một trong số đó. SFTP là protocol truyền file sử dụng subsystem sftp của sshd trên cùng kết nối đó, còn scp đã sử dụng protocol SFTP bên dưới kể từ OpenSSH 9.0. Port forwarding và Git over SSH cũng là các channel trên cùng kết nối. Tất cả đều dùng cùng một cổng, cùng kiểm tra host key và cùng thông tin đăng nhập. Lưu ý rằng SFTP không phải FTP được bổ sung mã hóa; protocol đó có tên là FTPS và là một protocol riêng.
Key authentication có thực sự tốt hơn mật khẩu không?
Có, với mọi server có thể truy cập từ Internet. Mật khẩu là một secret ngắn mà bạn gửi cho server trong mỗi lần đăng nhập, còn cổng 22 liên tục bị các client tự động dò đoán. Với một key pair, private key không bao giờ rời khỏi máy của bạn: client ký dữ liệu gắn với session hiện tại, còn server kiểm tra chữ ký đó với public key trong ~/.ssh/authorized_keys. Chữ ký đã ghi lại không thể được replay để đăng nhập vào server khác. Hãy bảo vệ private key bằng passphrase, vì file key không có passphrase sẽ cho phép bất kỳ ai sao chép được file đó đăng nhập vào server.