SSH là gì?
SSH, hay secure shell, vừa là một giao thức bảo mật vừa là phương thức phổ biến nhất để quản trị an toàn cho các máy chủ từ xa. Thông qua các công nghệ mã hóa, SSH cung cấp cơ chế để thiết lập một kết nối an toàn về mặt mật mã giữa hai bên, xác thực mỗi bên với nhau và truyền lệnh cũng như kết quả qua lại.
Trong bài hướng dẫn này, chúng ta sẽ xem xét các kỹ thuật mã hóa nền tảng và các phương pháp mà SSH sử dụng để thiết lập kết nối an toàn. Thông tin này có thể giúp chúng ta hiểu rõ các lớp mã hóa khác nhau và các bước cần thiết để thiết lập kết nối và xác thực cả hai bên.
Tìm hiểu về mã hóa đối xứng, mã hóa bất đối xứng và hàm băm
Để đảm bảo an toàn cho quá trình truyền tải thông tin, SSH áp dụng nhiều kỹ thuật xử lý dữ liệu khác nhau tại các giai đoạn khác nhau trong phiên kết nối. Chúng bao gồm các hình thức mã hóa đối xứng (symmetrical encryption), mã hóa bất đối xứng (asymmetrical encryption) và hàm băm (hashing).
Mã hóa đối xứng
Mối quan hệ giữa các thành phần thực hiện mã hóa và giải mã dữ liệu là yếu tố xác định lược đồ mã hóa đó thuộc loại đối xứng hay bất đối xứng.
Mã hóa đối xứng là một loại mã hóa mà trong đó chỉ cần một khóa duy nhất để mã hóa thông điệp gửi đi và cũng để giải mã thông điệp nhận về. Điều này đồng nghĩa với việc bất kỳ ai sở hữu khóa này đều có thể mã hóa và giải mã thông tin tới bất kỳ người nào khác cũng đang giữ khóa đó.
Kiểu mã hóa này thường được gọi là mã hóa bí mật chia sẻ (shared secret) hoặc khóa bí mật (secret key). Thông thường, chỉ một khóa duy nhất được sử dụng cho mọi thao tác, hoặc một cặp khóa có mối liên hệ rõ ràng và rất dễ dàng suy ra khóa còn lại.
SSH sử dụng khóa đối xứng để mã hóa toàn bộ kết nối. Trái với suy nghĩ của một số người dùng, các cặp khóa công khai/riêng tư (public/private asymmetrical key pairs) chỉ phục vụ cho nhu cầu xác thực, chứ không dùng để mã hóa kết nối. Mã hóa đối xứng bảo vệ quá trình xác thực bằng mật khẩu không bị nghe lén.
Cả client và server đều tham gia vào việc thiết lập khóa và secret keyđược tạo ra sẽ không bao giờ bị lộ cho bên thứ ba. Quá trình tạo khóa này được thực hiện thông qua một thuật toán gọi là trao đổi khóa (key exchange algorithm). Cơ chế trao đổi cho phép server và client tạo ra cùng một khóa bằng cách chia sẻ một số dữ liệu công khai và kết hợp với dữ liệu bí mật của riêng mình. Phần tiếp theo sẽ trình bày chi tiết về quy trình này.
Khóa mã hóa đối xứng được tạo ra trong bước này là khóa phiên (session-based) và cấu thành phần mã hóa thực sự cho toàn bộ dữ liệu trao đổi giữa server và client. Sau khi khóa được thiết lập, toàn bộ dữ liệu còn lại đều phải được mã hóa bằng shared secret này. Quá trình mã hóa diễn ra trước khi xác thực client.
SSH có thể được cấu hình để sử dụng nhiều hệ thống mật mã (cipher) đối xứng khác nhau, bao gồm Advanced Encryption Standard (AES), Blowfish, 3DES, CAST128, và Arcfour. Cả server và client đều có thể quyết định một danh sách các cipher mà họ hỗ trợ, sắp xếp theo mức độ ưu tiên. Lựa chọn đầu tiên trong danh sách của client mà có sẵn trên server sẽ được sử dụng làm thuật toán cipher cho cả hai chiều.
Trên Ubuntu 20.04, cả client và server đều được mặc định như sau:
chacha20-poly1305@openssh.com
aes128-ctr
aes192-ctr
aes256-ctr
aes128-gcm@openssh.com
aes256-gcm@openssh.com
Điều này có nghĩa là nếu hai máy Ubuntu 20.04 thiết lập kết nối với nhau (mà không ghi đè thuật toán mã hóa mặc định qua cấu hình), thì mặc định chúng sẽ sử dụng chacha20-poly1305@openssh.com
để mã hóa kết nối.
Mã hóa bất đối xứng
Mã hóa bất đối xứng khác với mã hóa đối xứng ở chỗ, chỉ truyền dữ liệu theo một chiều, cần sử dụng hai khóa có liên quan: một khóa riêng tư (private key) và một khóa công khai (public key).
Khóa công khai có thể chia sẻ công khai với bất kỳ ai. Nó được liên kết với một khóa cặp tương ứng, nhưng không thể dùng khóa công khai để suy ra khóa riêng tư. Nhờ mối quan hệ toán học đặc biệt giữa hai khóa, khóa công khai có thể mã hóa các thông điệp mà chỉ có khóa riêng tư mới có thể giải mã. Đây là một cơ chế một chiều, nghĩa là khóa công khai không thể giải mã các thông điệp do chính mình viết, cũng không thể giải mã thông tin được gửi từ khóa riêng tư.
Khóa riêng tư cần được giữ tuyệt mật và không được chia sẻ với bất kỳ ai. Đây là nguyên tắc then chốt để mô hình khóa công khai hoạt động hiệu quả. Chỉ có khóa riêng tư mới có thể giải mã các thông điệp được mã hóa bằng khóa công khai tương ứng. Do đó, bất kỳ bên nào giải mã được các thông điệp đó đều chứng minh rằng họ đang sở hữu khóa riêng tư.
SSH sử dụng mã hóa bất đối xứng ở một số bước, điển hình là trong quá trình trao đổi khóa ban đầu nhằm thiết lập mã hóa đối xứng (dùng để mã hóa phiên). Ở giai đoạn này, cả hai bên đều tạo ra các cặp khóa tạm thời và trao đổi khóa công khai để cùng tính toán ra một shared secret, sẽ được sử dụng cho mã hóa đối xứng.
Công dụng nổi bật của mã hóa bất đối xứng trong SSH là cơ chế xác thực bằng SSH key. Client có thể tạo một cặp khóa và tải khóa công khai lên các máy chủ từ xa mà nó muốn truy cập. Khóa này được lưu trong tệp authorized_keys
, thư mục ~/.ssh
nằm trong thư mục chính của tài khoản người dùng trên máy chủ từ xa.
Sau khi mã hóa đối xứng đã được thiết lập để bảo mật kênh liên lạc giữa server và client, client phải xác thực để được phép truy cập. Server sử dụng khóa công khai trong file này để mã hóa một thông điệp thử thách (challenge message) và gửi lại cho client. Nếu client giải mã được chuỗi này, tức là nó đang nắm giữ khóa riêng tư tương ứng. Khi đó, server sẽ tiến hành thiết lập môi trường phiên cho client.
Hashing
Một kỹ thuật xử lý dữ liệu quan trọng khác được SSH sử dụng là hashing mật mã (cryptographic hashing). Các hàm hash mật mã có nhiệm vụ tạo ra một “chữ ký” hoặc bản tóm tắt ngắn gọn của một tập hợp dữ liệu. Các đặc tính chính của hàm hash mật mã là: không thể đảo ngược, gần như không thể bị can thiệp một cách có chủ đích, và kết quả đầu ra gần như luôn là duy nhất.
Khi sử dụng cùng một hàm hash và cùng một thông điệp, kết quả trả về luôn giống nhau và chỉ cần sửa đổi bất kỳ phần nào của dữ liệu cũng sẽ tạo ra một giá trị hash hoàn toàn khác. Mặc dù không thể khôi phục lại thông điệp gốc từ giá trị hash, nhưng có thể kiểm tra xem một thông điệp cụ thể có tạo ra giá trị hash tương ứng hay không.
Với những thuộc tính này, hash thường được sử dụng để đảm bảo tính toàn vẹn dữ liệu và xác minh tính xác thực trong giao tiếp. Trong SSH, hash chủ yếu được ứng dụng thông qua HMAC (Hash-based Message Authentication Code). Chúng được sử dụng để xác thực rằng thông điệp nhận được là nguyên vẹn và không bị sửa đổi.
Trong quá trình đàm phán mã hóa đối xứng đã đề cập trước đó, một thuật toán MAC sẽ được chọn ra từ danh sách các phương án MAC do client đề xuất. Phương án đầu tiên trong danh sách mà server hỗ trợ sẽ được sử dụng.
Mỗi tin nhắn được gửi đi sau khi đã thiết lập mã hóa đều phải đi kèm một giá trị MAC để bên nhận có thể xác minh tính toàn vẹn của gói tin (packet). MAC được tính toán dựa trên khóa bí mật đối xứng, số thứ tự của gói tin (packet sequence number) và nội dung thực tế của tin nhắn.
Bản thân MAC được gửi bên ngoài khu vực được mã hóa đối xứng như là phần cuối cùng của gói tin. Cách làm này được nhiều chuyên gia khuyến nghị: mã hóa dữ liệu trước, sau đó mới tính toán và đính kèm MAC.
Nguyên lý hoạt động của SSH
Có thể bạn đã nắm một số điểm cơ bản về cách thức hoạt động của SSH. Giao thức SSH áp dụng mô hình client-server để xác thực hai bên tham gia và mã hóa dữ liệu truyền giữa chúng.
Thành phần server lắng nghe kết nối trên một cổng (port) được chỉ định. Nó chịu trách nhiệm đàm phán kết nối bảo mật, xác thực bên kết nối, và khởi tạo môi trường phù hợp nếu xác thực thành công.
Thành phần client khởi tạo quá trình bắt tay TCP (Transmission Control Protocol) với server, đàm phán kết nối bảo mật, xác minh danh tính server dựa trên thông tin đã lưu trữ trước đó, và cung cấp thông tin cần thiết để xác thực.
Một phiên SSH được thiết lập qua hai giai đoạn riêng biệt. Giai đoạn đầu tiên là thống nhất và thiết lập mã hóa để bảo vệ giao tiếp trong tương lai. Giai đoạn thứ hai là xác thực người dùng để xác định quyền truy cập vào hệ thống.
Đàm phán mã hóa cho phiên làm việc
Khi client thiết lập kết nối TCP, server sẽ phản hồi bằng các phiên bản giao thức mà nó hỗ trợ. Trong số các phiên bản giao thức được chấp nhận, nếu client tìm được một phiên bản tương thích thì kết nối sẽ được tiếp tục. Đồng thời, server cũng sẽ gửi khóa host công khai để client có thể sử dụng để xác minh danh tính host đích.
Tại thời điểm này, cả hai bên tiến hành đàm phán một khóa phiên (session key) bằng một biến thể của thuật toán Diffie-Hellman. Thuật toán này (và các biến thể của nó) cho phép mỗi bên kết hợp dữ liệu riêng của mình với dữ liệu công khai của bên còn lại để đi đến một khóa phiên bí mật giống hệt nhau.
Khóa phiên sẽ được sử dụng để mã hóa toàn bộ phiên làm việc. Các cặp khóa công khai và riêng tư được dùng trong quá trình này hoàn toàn tách biệt với các khóa SSH được sử dụng để xác thực một client với server.
Cơ sở của quy trình này đối với Diffie-Hellman cổ điển là:
- Cả hai bên thống nhất về một số nguyên tố lớn, sẽ đóng vai trò là giá trị gốc.
- Cả hai bên thống nhất về một bộ tạo mã hóa (thường là AES), sẽ được sử dụng để xử lý các giá trị theo cách đã được định trước.
- Mỗi bên tự nghĩ ra một số nguyên tố khác và giữ bí mật với bên kia. Con số này được sử dụng làm khóa riêng tư cho tương tác này (khác với khóa SSH riêng tư được sử dụng để xác thực).
- Khóa riêng tư, bộ tạo mã hóa và số nguyên tố chung được dùng để tạo ra một khóa công khai ****(được suy ra từ khóa riêng tư) ****rồi chia sẻ với bên kia.
- Sau đó cả hai bên trao đổi các khóa công khai đã tạo.
- Bên nhận sử dụng khóa riêng tư của chính mình, khóa công khai của bên kia và số nguyên tố chung ban đầu để tính toán ra một shared secret key. Mặc dù quá trình này được tính toán độc lập, sử dụng các khóa riêng tư và các khóa công khai đối lập nhưng kết quả cuối cùng vẫn là giống hệt nhau.
- Shared secret được sử dụng để mã hóa tất cả các giao tiếp sau đó.
Quá trình này đảm bảo cả hai bên tham gia một cách bình đẳng vào việc tạo ra shared secret, đồng thời ngăn không cho bên nào đơn phương kiểm soát khóa này. Nó cũng hoàn thành nhiệm vụ tạo ra mộtshared secret giống hệt nhau mà không phải truyền thông tin đó qua các kênh không an toàn. Mã hóashared secret sẽ được sử dụng để mã hóa toàn bộ kết nối, thông qua cơ chế gọi là giao thức gói nhị phân (binary packet protocol).
Shared secret được tạo ra là một khóa đối xứng, nghĩa là cùng một khóa có thể dùng để mã hóa và giải mã dữ liệu của bên còn lại. Mục đích của việc này là để gói gọn tất cả các giao tiếp tiếp theo vào trong một đường hầm được mã hóa, ngăn không cho bên ngoài giải mã.
Sau khi mã hóa phiên được thiết lập, giai đoạn tiếp theo là xác thực người dùng.
Xác thực quyền truy cập của người dùng vào server
Bước tiếp theo là xác thực người dùng và quyết định quyền truy cập. Có một vài phương pháp có thể được sử dụng để xác thực, dựa trên những gì server cho phép.
Phương pháp phổ biến là xác thực bằng mật khẩu, tức là khi server yêu cầu client cung cấp mật khẩu của tài khoản mà họ muốn đăng nhập. Mật khẩu được truyền qua kênh mã hóa đã được thiết lập trước đó nên đảm bảo an toàn trước các bên thứ ba.
Tuy nhiên, mặc dù mật khẩu sẽ được mã hóa nhưng phương pháp này thường không được khuyến nghị do những hạn chế về độ phức tạp của mật khẩu. Các tập lệnh tự động hóa (Automated scripts) có thể dễ dàng bẻ khóa những loại mật khẩu có độ dài thông thường.
Giải pháp thay thế phổ biến và được khuyến nghị nhiều nhất là sử dụng cặp khóa SSH. Cặp khóa SSH là các khóa bất đối xứng, nghĩa là hai khóa liên quan phục vụ các chức năng khác nhau.
Khóa công khai được sử dụng để mã hóa dữ liệu nhưng chỉ có thể được giải mã bằng khóa riêng tư. Khóa công khai có thể được chia sẻ tự do, vì mặc dù nó có thể mã hóa cho khóa riêng tư nhưng không thể từ khóa công khai để suy ra khóa riêng tư.
Quy trình xác thực bằng cặp khóa SSH bắt đầu sau khi mã hóa đối xứng đã được thiết lập, như đã trình bày ở phần trước. Cụ thể, các bước trong quy trình này bao gồm:
- Client gửi một ID của cặp khóa mà nó muốn sử dụng để xác thực đến server.
- Server kiểm tra file
authorized_keys
của tài khoản mà client muốn đăng nhập để tìm kiếm ID của khóa. - Nếu có một khóa công khai có ID khớp được tìm thấy trong file, server sẽ tạo ra một số ngẫu nhiên và mã hóa số này bằng khóa công khai.
- Server gửi tin nhắn đã mã hóa này trở lại cho client.
- Nếu client thực sự có khóa riêng tư tương ứng thì nó sẽ giải mã tin nhắn bằng khóa đó và lấy ra số ban đầu.
- Client kết hợp số đã giải mã với khóa phiên chung (shared session key) đang được sử dụng để mã hóa giao tiếp, và tính toán hash MD5 của giá trị này. MD5 là một hàm băm mật mã học (message-digest algorithm) được sử dụng để tạo ra một giá trị hash 128-bit từ hàm hash.
- Client gửi giá trị hash MD5 này trở lại cho server, như một phản hồi cho thông điệp số đã mã hóa.
- Server sử dụng cùng một khóa phiên chung và số ngẫu nhiên ban đầu mà nó đã gửi cho client để tự tính toán giá trị MD5, rồi so sánh kết quả của mình với giá trị mà client đã gửi. Nếu hai giá trị này khớp nhau, điều đó chứng minh rằng client đã sở hữu khóa riêng tư hợp lệ và xác thực thành công.
Tóm lại, tính bất đối xứng của cặp khóa cho phép server mã hóa thông điệp cho client bằng khóa công khai. Sau đó, client có thể chứng minh rằng mình đang giữ khóa riêng tư tương ứng bằng cách giải mã tin nhắn một cách chính xác. Hai loại mã hóa được sử dụng (bí mật chia sẻ đối xứng và khóa công khai/khóa riêng tư bất đối xứng) mỗi loại đều có thể phát huy thế mạnh cụ thể của mình trong mô hình này.
Kết luận
Việc tìm hiểu các bước đàm phán kết nối và các lớp mã hóa được sử dụng trong SSH sẽ giúp bạn hiểu rõ hơn về những gì đang thực sự diễn ra khi đăng nhập vào một máy chủ từ xa. Giờ đây, bạn đã có thể nhận diện mối quan hệ giữa các thành phần và thuật toán khác nhau, đồng thời hình dung được cách ghép nối tất cả các mảnh ghép này lại với nhau thành một thể thống nhất.