Server không biết mặt tôi.
Nó không biết giọng tôi, không biết đêm qua tôi ngủ mấy tiếng, cũng không biết tôi còn làm ở công ty hay đã nghỉ từ tháng trước. Về tôi, nó biết đúng một điều: ở đầu dây bên kia, có ai đó vừa làm được một phép tính mà lẽ ra chỉ người giữ một file nhất định mới làm nổi. Thế là đủ. Cổng mở, dấu nhắc lệnh hiện ra, con trỏ nhấp nháy với vẻ ngoan ngoãn của một đạo quân chờ lệnh.
Con người ít khi dám tin nhau kiểu ấy. Ngân hàng còn đòi xem căn cước, bảo vệ chung cư còn hỏi lên tầng mấy. Vậy mà ta không gọi SSH là cả tin, ta gọi nó là chuẩn mực, và ta có lý. Tôi chỉ tò mò một chuyện: lúc cổng mở, chính xác thì điều gì vừa được chứng minh? Và điều gì ta tưởng đã được chứng minh mà thật ra chưa?
Câu hỏi này có một tiền lệ rất xa, đúc bằng đồng, hình con hổ.
Con hổ bổ đôi
Hổ phù là tín vật điều binh của Trung Hoa cổ. Bảo tàng Met có một chiếc bằng đồng từ đời Hán, và lời mô tả của họ gói cả cơ chế vào vài dòng: con hổ gồm hai nửa ăn khớp với nhau, hoàng đế giữ một nửa, viên tướng cầm quân giữ nửa kia. Lệnh điều quân phải đi kèm nửa của hoàng đế. Ghép lại mà khớp thì lệnh được coi là thật.
Tôi thích thiết kế này vì cái cách nó lách qua một câu hỏi khó. Điều viên tướng ngoài biên ải thật sự cần biết là: lệnh này có đúng là ý vua không? Vua thì ở xa, chữ viết thì giả được, sứ giả thì mua được. Hổ phù không trả lời câu ấy. Nó lặng lẽ đổi sang một câu dễ hơn nhiều: hai miếng đồng này có khớp nhau không?
Tôi ngờ rằng phần lớn cơ chế xác thực, từ con dấu đỏ đến thẻ từ, đều diễn đúng một trò ảo thuật ấy: tráo một câu hỏi về ý chí thành một câu hỏi về đồ vật. Trò tráo hữu ích đến mức ta quên rằng nó là một trò tráo. Phần lớn thời gian, hai câu hỏi có chung đáp án, vì thường thì ai cầm nửa con hổ của vua cũng là người vua sai đi.
Hợp phù, nghi chi
Một ngoại lệ nổi tiếng được chép trong Sử ký, Ngụy công tử liệt truyện .
Quân Tần vây kinh đô nước Triệu. Vua Ngụy sai tướng Tấn Bỉ đem quân đi cứu, rồi sợ Tần nên bắt dừng giữa đường. Công tử nước Ngụy là Tín Lăng Quân muốn cứu Triệu mà trong tay không có quân. Môn khách Hầu Doanh bày kế: binh phù để trong phòng ngủ của vua, và Như Cơ, người được vua sủng ái lại từng chịu ơn công tử, ra vào được chỗ ấy. Như Cơ lấy trộm phù. Tín Lăng Quân mang nó đến doanh trại, giả lệnh vua đòi thay tướng. Tấn Bỉ ghép phù. Phù khớp. Ông vẫn ngờ, giơ tay nhìn công tử mà hỏi: ta giữ đại quân nơi biên giới, nay ngài đi một cỗ xe đến thay, thế là thế nào? Ông toan không nghe theo. Chu Hợi, người theo công tử, dùng quả chùy sắt giấu trong tay áo đánh chết ông. Tín Lăng Quân nắm lấy đạo quân, kéo đi cứu Triệu.
Tư Mã Thiên chỉ dùng bốn chữ cho khoảnh khắc quan trọng nhất: 合符,疑之 (hợp phù, nghi chi). Ghép phù rồi, vẫn ngờ.
Tôi đọc đi đọc lại bốn chữ này. Miếng đồng khớp, tức là người đứng trước mặt Tấn Bỉ đang cầm nửa con hổ của vua. Nó không nói gì về chuyện vua có biết hay không. Tấn Bỉ hiểu điều đó, và về mặt sự thật thì ông ngờ trúng: vua Ngụy chưa bao giờ ra lệnh ấy. Người thứ hai hiểu điều đó, trớ trêu thay, là kẻ bày mưu. Hầu Doanh đã lường trước rằng phù có khớp thì Tấn Bỉ vẫn có thể không giao quân mà xin lệnh vua lần nữa, nên mới tiến cử Chu Hợi đi cùng. Nghe đến đấy, Tín Lăng Quân khóc, vì biết vị lão tướng sẽ không dễ nghe và hẳn sẽ phải chết.
Đọc bằng con mắt của người trực hệ thống, đây gần như một bản báo cáo sự cố. Tín vật bị lấy bởi người có quyền hợp lệ ra vào nơi cất giữ. Lớp kiểm soát cuối cùng là một con người biết ngờ, và lớp ấy bị vô hiệu hóa bằng một quả chùy. Đến mục "nguyên nhân gốc", tôi sẽ phải điền: lòng biết ơn. Chưa mô hình phân quyền nào có cột cho thứ đó.
Nhưng mẫu báo cáo cũng không có mục nào cho phần còn lại. Kẻ "xâm nhập" đem quân đi cứu một kinh thành đang bị vây. Người làm đúng phận sự thì chết. Người phá quy trình thì được Tư Mã Thiên dành riêng một thiên liệt truyện. Tôi không biết phải xử vụ này thế nào, và tôi ngờ rằng người chép sử cố ý không xử hộ.
Chỉ một điều thì chắc: suốt câu chuyện, con hổ đồng không hề trục trặc. Nó làm đúng việc của nó. Nó khớp.
Nửa con hổ không rời nhà
Xác thực bằng public key trong SSH là hậu duệ của con hổ ấy, kèm một cải tiến mà vua Ngụy hẳn sẽ muốn có.
Bạn tạo một cặp khóa. Private key ở lại máy bạn. Public key được chép vào file authorized_keys của một tài khoản trên server, rồi nằm đó như nửa con hổ trong doanh trại. Đến lúc đăng nhập, theo RFC 4252 , máy bạn không gửi private key đi đâu cả. Nó dùng private key ký lên một gói dữ liệu gồm mã định danh của chính phiên kết nối này cùng các trường của yêu cầu đăng nhập, như tên tài khoản và public key định dùng. Server kiểm tra hai việc: public key ấy có được chấp nhận cho tài khoản này không, và chữ ký có đúng không. Vì chữ ký dính liền với mã định danh của phiên, ai chép được nó cũng không đem sang một phiên mới mà dùng lại được.
Mật khẩu thì khác ở đâu? Trong SSH, mật khẩu cũng đi trong kênh đã mã hóa chứ không phơi trần trên đường truyền. Nhưng đến nơi, bên kia nhận nguyên vẹn chính cái bí mật ấy. Với public key, thứ được đưa ra không phải là thứ được giữ. Tín Lăng Quân phải cầm cả nửa con hổ đến tận doanh trại. Ở đây sứ giả đi tay không, chỉ để lại một dấu vết mà riêng nửa con hổ ấy mới tạo ra được, và dấu vết ấy chỉ có nghĩa trong đúng cuộc gặp này.
Phép so sánh xin dừng ở đây, trước khi nó làm hỏng việc. Public key không phải "một nửa" của private key. Chẳng có hai miếng nào ghép vào nhau, chỉ có toán. Người đời Hán không phát minh ra mật mã khóa công khai. Và hổ phù, như ta vừa thấy, hoàn toàn có thể bị trộm.
Vậy một chữ ký hợp lệ chứng minh điều gì? Tôi thử viết ra cho thật chặt: trong phiên này, có một thứ gì đó dùng được private key tương ứng. Hết.
"Dùng được" rộng hơn "sở hữu" khá nhiều. Nó gồm cả người đã chép được file ấy. Cả phần mềm lạ đang chạy dưới tên bạn. Cả cái script chạy lúc ba giờ sáng theo ý muốn của bạn từ sáu tháng trước, mà tôi không chắc ý muốn sáu tháng trước có còn tính là ý muốn không. Rồi còn `ssh-agent` , chương trình giữ sẵn khóa để bạn khỏi gõ passphrase mỗi lần. Nếu bạn chuyển tiếp agent sang một máy khác, ai kiểm soát được máy ấy có thể nhờ agent ký hộ chừng nào kết nối còn đó. Họ không lấy được khóa. Họ chỉ mượn con hổ, và server thì không phân biệt được mượn với cầm.
Tức là ta có một câu trả lời rất chắc cho một câu hỏi rất hẹp. Người ấy là ai, có nên vào không, vào để làm gì: giao thức không hỏi.
Thoạt nghe thì như một khiếm khuyết. Nhưng thử hình dung điều ngược lại, một server biết ngờ. Hai giờ sáng, hệ thống sập, bạn đăng nhập để cứu, và sshd giơ tay: "Giờ này anh đến một mình, thế là thế nào?" Tôi không chắc mình muốn làm việc với một Tấn Bỉ bằng silicon. Sự ngờ vực của ông vừa đúng, vừa là thứ đứng giữa đạo quân và một kinh thành đang bị vây. Một cỗ máy không biết ngờ thì ít ra cũng không ngờ nhầm.
Vậy là ta đã chọn: server không ngờ. Nhưng sự ngờ vực không biến mất. Nó chỉ dọn đi nơi khác, đến những chỗ con người còn chen tay vào được.
Phòng ngủ của vua
Vua Ngụy cất binh phù ở nơi kín nhất ông có. Và nó bị lấy bởi người có quyền bước vào đúng nơi ấy. Tôi thấy chi tiết này đáng giá hơn khối bài giảng về an toàn thông tin.
Private key của ta cũng có phòng ngủ. Trang hướng dẫn của ssh nói rõ file khóa không được để người dùng khác trên máy chạm tới, và ssh sẽ lờ đi một file khóa hớ hênh kiểu đó. Thư mục .ssh chỉ mình vào, file khóa chỉ mình đọc ghi, tức là cặp số 700 và 600 quen thuộc. Đó là cánh cửa. Passphrase là cái tráp khóa đặt sau cánh cửa: theo ssh-keygen , nó mã hóa private key khi khóa nằm trên đĩa, nên kẻ chép được file còn phải qua thêm một lớp nữa.
Có điều cả cửa lẫn tráp đều chỉ chặn người ngoài. Phần mềm độc hại chạy dưới chính tài khoản của bạn là Như Cơ: nó không phá cửa vì nó vốn ở trong phòng. Nó đọc được thứ bạn đọc được, có thể rình lúc bạn gõ passphrase, có thể nhờ cái agent đang mở ký hộ. Passphrase khiến kẻ trộm tốn công hơn chứ không biến bản sao thành vô dụng. Và nếu một bản sao đã lọt ra ngoài, đổi passphrase chỉ là thay khóa cái tráp của mình. Cái tráp kẻ kia đang cầm vẫn mở bằng passphrase cũ.
Lời hứa không có ngày hết hạn
Bên phía server, mỗi dòng trong authorized_keys là một lời hứa: ai mang đến chữ ký khớp với khóa này thì được vào. Public key tự nó chẳng có gì phải giấu. Thứ phải giữ là quyền viết vào file ấy, vì ai thêm được một dòng là tự đúc cho mình một con hổ.
Hãy hình dung một công ty không có thật, và file authorized_keys trên máy chủ chính của họ sau năm năm. Nó giống một lớp trầm tích. Có dòng mang ghi chú khoa@macbook-cu, mà Khoa nghỉ từ năm kia và cái MacBook đã về tay ai không rõ. Có dòng freelancer-web, không ai nhớ là freelancer nào. Có dòng deploy-tam-thoi, và như mọi thứ được đặt tên là tạm thời trong hạ tầng, nó thọ nhất. Dòng nào cũng vẫn là một lời hứa còn hiệu lực. Lời hứa không biết người nhận đã đi đâu.
Lời hứa ấy thường cũng rộng hơn cần thiết. sshd cho phép ghi điều kiện ngay đầu mỗi dòng: restrict tắt một loạt khả năng như chuyển tiếp cổng, chuyển tiếp agent và cấp terminal, command="..." buộc khóa ấy chỉ chạy một lệnh định sẵn, from="..." giới hạn địa chỉ nguồn, expiry-time="..." đặt ngày hết hạn trên các bản OpenSSH hiện hành. Chúng bổ sung cho nhau chứ không thay nhau: một lệnh ép buộc đứng riêng chưa chặn được chuyện chuyển tiếp. Và khóa nào cũng chỉ làm được những gì tài khoản đứng sau nó làm được. Khóa vào thẳng root là hổ phù điều cả ba quân, dù người cầm chỉ định ghé xem log.
Chia nhỏ còn một cái lợi nữa, lộ ra vào ngày xấu trời. Khi mỗi người, mỗi máy có khóa riêng, mất một chiếc laptop thì ta biết phải gạch dòng nào. Nó không cứu được mọi thứ, vì một máy bị chiếm vẫn mất mọi khóa nằm trên nó. Nhưng khi cả nhóm dùng chung một khóa, lộ một bản là lộ tất cả, mà muốn thu hồi cũng không biết thu của ai.
Nói đến thu hồi, đây là chỗ tôi thấy trực giác hay trượt nhất. Thử bịa thêm một đoạn mà Sử ký không chép: vua Ngụy phát hiện mất phù ngay trong đêm. Ông làm gì? Lục tung phòng ngủ thì vô ích. Thay khóa cửa phòng cũng vô ích. Việc duy nhất còn có nghĩa là cho ngựa phi thật nhanh đến doanh trại, báo với Tấn Bỉ rằng từ giờ nửa con hổ kia không còn giá trị. Quyền lực của hổ phù chưa bao giờ nằm trong phòng ngủ. Nó nằm ở chỗ doanh trại còn chịu nhận.
Với SSH cũng vậy. Khi ngờ một private key đã lộ, xóa file trên máy mình chỉ khiến mình mất khóa, còn kẻ giữ bản sao thì không mất gì. Việc phải làm nằm bên phía server: gỡ dòng public key tương ứng khỏi mọi authorized_keys từng nhận nó, rồi tạo cặp khóa mới. Dòng ấy còn thì lời hứa còn.
Con hổ quay mặt lại
Có một câu hỏi Tín Lăng Quân không bao giờ phải đặt ra: doanh trại trước mặt có đúng là doanh trại của Tấn Bỉ không? Dựng giả một trại quân thì khó. Dựng giả một server thì rẻ hơn nhiều.
Nên SSH có con hổ thứ hai, quay mặt về phía ngược lại. Server cũng có cặp khóa của riêng nó, gọi là host key, để chứng minh với người đến rằng nó đúng là nó. Hai vai này không thay được cho nhau: khóa của người dùng trả lời "ai đang xin vào", host key trả lời "mình đang gõ cửa nhà ai". Lần đầu kết nối tới một máy lạ, ssh đưa ra fingerprint của host key và hỏi bạn có muốn tiếp tục không. Nếu bạn đồng ý, khóa ấy được ghi vào known_hosts, và theo mặc định của ssh_config , từ đó về sau ssh sẽ từ chối kết nối khi thấy khóa của máy ấy đổi khác.
Khoảnh khắc ấy là lần hiếm hoi có người được đứng vào chỗ của Tấn Bỉ, và người đó là ta. Giơ tay ở đây nghĩa là đem fingerprint đối chiếu qua một đường khác mà mình tin được, như bảng điều khiển của nhà cung cấp hay chính người dựng máy, chứ không phải qua cái kết nối đang cần kiểm chứng. Còn khi ssh báo host key đã thay đổi, câu đáng hỏi là vì sao, trước khi nghĩ đến chuyện xóa dòng cũ cho hết phiền. Có thể máy vừa được cài lại. Cũng có thể không.
Bận tâm làm gì, khi private key của ta đằng nào cũng không rời máy? Vì một server giả không cần khóa của ta. Nó chỉ cần ta tin mình đã vào đúng chỗ, rồi ngồi nghe mọi thứ ta gõ sau đó.
Tấn Bỉ ngờ khi mọi thứ đều khớp. Ta thì hay tin khi chưa có gì để khớp.
Con hổ trong bảo tàng
Còn một điều tôi chưa xếp được vào ngăn nào. Công việc thật cũng có những đêm của Tín Lăng Quân: việc gấp, người có quyền thì không liên lạc được, người liên lạc được thì không có quyền. Một hệ thống tốt là hệ thống chặn đứng được ông, hay là hệ thống để ông đi nhưng sáng hôm sau ai cũng biết ông đã đi, bằng khóa nào, vào lúc mấy giờ? Tôi nghiêng về vế sau, rồi lại nhớ ra rằng cánh cửa mở cho Tín Lăng Quân thì cũng mở cho những người kém chính nghĩa hơn ông nhiều. Miếng đồng không phân biệt được hai loại ấy. Chữ ký cũng không.
Con hổ của Met bây giờ nằm trong bộ sưu tập. Chẳng còn hoàng đế nào giữ nửa này, chẳng còn viên tướng nào giữ nửa kia, và không đạo quân nào chờ chúng khớp nhau. Đó là kiểu thu hồi triệt để nhất tôi biết: cả vương triều đứng sau lời hứa đã không còn. Con hổ còn, quyền thì hết.
File authorized_keys lâu năm thì ngược lại. Người đã đi, máy đã bán, mà vương quốc vẫn một mực giữ lời.
Tôi vẫn chưa trả lời được câu hỏi của Tấn Bỉ, và càng nghĩ càng ngờ rằng câu trả lời không nằm trong miếng đồng hay trong chữ ký. Phù khớp cho ta biết ai đang cầm. Còn ai sai đi, đi để làm gì, và có nên đi không, thì phải hỏi ở chỗ khác: lúc viết một dòng cấp quyền, lúc gạch nó đi, lúc đứng trước một fingerprint lạ.
Server không có tay để giơ. Chúng ta thì có, dù phần lớn thời gian hai bàn tay ấy đang bận gõ yes.
No comments yet