1 điểm bởi GN⁺ 4 giờ trước | 1 bình luận | Chia sẻ qua WhatsApp
  • Token bảo mật ký bên trong thiết bị mà không xuất khóa riêng tư ra ngoài, đồng thời yêu cầu thao tác vật lý từ người dùng nên kẻ tấn công từ xa khó tạo chữ ký tùy ý
  • Có thể dùng cho xác thực SSH, U2F, đăng nhập cục bộ không mật khẩu, sudo, ký commit git, và phần cứng bảo mật tích hợp trên laptop/điện thoại thông minh hiện đại có thể thay thế YubiKey
  • Tệp “khóa riêng tư” được tạo bằng ssh-keygen -t ed25519-sk thực ra không phải khóa riêng tư thật mà là một handle trỏ tới khóa trong token, và có thể tạo cùng một tệp khóa SSH trên máy tính khác với cùng token đó
  • Trên MacBook, có thể cấu hình secure element làm khóa SSH để đăng nhập SSH bằng Touch ID, còn với ký commit git thì cần cấu hình user.signingKey theo dạng ssh-agentkey:: thay vì đường dẫn tệp
  • Token bảo mật không thể khôi phục khóa riêng tư nếu bị mất và có rủi ro về trải nghiệm sử dụng khi người dùng quen với việc chạm lặp lại; trên laptop Windows, Windows Hello có thể xác nhận việc dùng khóa SSH bằng nhận diện khuôn mặt, vân tay hoặc PIN

Ưu điểm và giới hạn của token bảo mật

  • Cấu trúc cốt lõi để ngăn tấn công từ xa

    • Token bảo mật là thiết bị giữ cặp khóa riêng tư/công khai bên trong, trong đó khóa công khai có thể dễ dàng lấy ra nhưng khóa riêng tư không rời khỏi thiết bị
    • Khi gửi gói dữ liệu cần ký tới thiết bị, dữ liệu sẽ được ký bên trong thiết bị bằng khóa riêng tư, và thường cần thao tác vật lý của người dùng như nhấn nút cảm ứng đang nhấp nháy
    • Ngay cả khi kẻ tấn công từ xa truy cập được vào máy tính, nếu người dùng không thực hiện thao tác trong thế giới thực thì token bảo mật cũng không ký tùy ý, nên cách này có vẻ tốt hơn việc lưu toàn bộ cặp khóa riêng tư/công khai SSH thành tệp trong thư mục ~/.ssh
    • Cũng có các lựa chọn dành cho người muốn firmware FOSS như SoloKeysNitrokeys
    • Các token bảo mật cao cấp hơn còn bổ sung sinh trắc học như đầu đọc vân tay tích hợp, nhưng cốt lõi vẫn là cấu trúc không để khóa riêng tư rời khỏi thiết bị
  • Rủi ro phát sinh từ tính tiện dụng

    • Nếu người dùng quen với việc nhấn token bảo mật mỗi khi nó nhấp nháy, họ có thể vô thức phản hồi cả những yêu cầu độc hại
    • Trong các tác vụ ký liên tục phải chạm token lặp đi lặp lại, rất khó thực sự nhận ra một yêu cầu nhấp nháy thêm lần nữa
    • Apple và Microsoft dùng cách hiển thị mã số ngẫu nhiên cho từng yêu cầu truy cập trong ứng dụng xác thực trên điện thoại để người dùng nhập lại, nhưng cách này khá phiền và làm giảm lợi thế về trải nghiệm của token bảo mật so với các ứng dụng Authy hay Google Authenticator dùng TOTP
  • Vấn đề mất thiết bị và sao lưu

    • Nếu làm mất token bảo mật, khóa riêng tư tương ứng sẽ biến mất vĩnh viễn và không có cách sao lưu
    • Để tránh nguy cơ bị khóa khỏi nhiều tài khoản, khi mua token bảo mật nên mua ít nhất 2 chiếc và đăng ký chúng với cùng dịch vụ
    • Một phương án khác là cách sao lưu/khôi phục bằng việc chuyển khóa riêng tư thành danh sách từ mà con người có thể đọc và ghi lại, như BIP 39
    • Nếu khóa riêng tư có thể rời khỏi secure enclave, cũng có thể xuất hiện tấn công lừa đảo khiến người dùng ghi danh sách từ đó ở nơi không an toàn
    • Nếu thực sự lo ngại khả năng mất hết mọi token bảo mật, danh sách từ BIP 39 có thể là phương án cuối cùng để giành lại quyền truy cập hệ thống

Dùng token bảo mật với SSH và git

  • Đặt khóa riêng tư SSH trong token bảo mật

    • Thông thường khi chạy ssh-keygen, một cặp tệp chứa đầy đủ khóa riêng tư sẽ được tạo ra
    • Để đặt khóa riêng tư trong token bảo mật, hãy cài libfido2 theo hướng dẫn FIDO/U2F của Yubico, rồi chạy ssh-keygen -t ed25519-sk khi token đang được cắm vào
    • Khi đó vẫn tạo ra một cặp tệp, nhưng tệp “khóa riêng tư” thực ra không phải khóa riêng tư thật mà là một handle trỏ tới khóa riêng tư nằm trong token bảo mật
    • Nếu chạy lại ssh-keygen -t ed25519-sk với cùng token bảo mật, có thể tạo cùng một cặp tệp khóa riêng tư/công khai trên bất kỳ máy nào, nên quyền truy cập SSH sẽ di chuyển cùng token bảo mật chứ không gắn với một tệp cụ thể trên một máy cụ thể
  • Xác thực git và ký commit

    • Khoảng 90% số lần phải chạm token bảo mật là do dùng git
    • Các git forge triển khai xác thực SSH cho thao tác push và pull, và chỉ cần tải lên tệp id_ed25519_sk.pub đã tạo ở trên là có thể cho phép cặp khóa của token bảo mật
    • git cũng hỗ trợ khóa SSH cho ký commit; sau khi làm theo thiết lập khóa ký bằng khóa SSH trong tài liệu GitHub rồi chạy git config --global commit.gpgsign true, mọi commit sẽ được tự động ký
    • Để git forge nhận commit là do chính bạn ký, cần tải khóa công khai lên thêm lần nữa; trường này thường tách biệt với trường dùng cho xác thực SSH
  • Sự bất tiện của ký commit

    • Khi rebase một danh sách commit dài, bạn phải ký lại toàn bộ commit
    • YubiKey có đầu đọc vân tay có tỷ lệ nhận sai quá cao để ký liên tục hàng chục commit, nên cuối cùng phải ngừng dùng
    • Trong jujutsu, một wrapper của git theo hướng “rebase/amend-first”, có cách chỉ ký commit tại thời điểm push
  • Đăng nhập cục bộ Linux và sudo

    • Trên hệ thống Linux, có thể dùng Pluggable Authentication Module(PAM) để dùng token bảo mật cho đăng nhập cục bộ không mật khẩu và nâng quyền sudo

Dùng secure element của MacBook làm khóa SSH

  • Nếu cứ cắm token bảo mật ở cổng USB-C, nó sẽ lòi ra như một đòn bẩy nhỏ có thể làm hỏng cả cổng lẫn token nếu vô tình va đập hoặc làm rơi
  • Trên MacBook Air M1 đời 2020, tác giả đã làm theo hướng dẫn của Arian van Putten để cấu hình phần tử bảo mật tích hợp làm khóa SSH
sc_auth create-ctk-identity -l ssh -k p-256-ne -t bio
ssh-keygen -w /usr/lib/ssh-keychain.dylib -K -N ""
  • Lệnh này tạo cặp tệp khóa riêng tư/công khai id_ecdsa_sk_rk, rồi chuyển các tệp đó vào thư mục ~/.ssh
  • Ở đây nữa, tệp khóa riêng tư không phải khóa riêng tư thật mà là handle tới khóa nằm trong thiết bị, nên có dạng có thể dán công khai
  • Để thêm khóa công khai vào máy chủ homelab làm authorized key, chạy như sau
ssh-copy-id -i ~/.ssh/id_ecdsa_sk_rk.pub <server nickname>
  • Sau đó thêm cấu hình sau vào ~/.ssh/config
Host *
  IdentityFile ~/.ssh/id_ecdsa_sk_rk
  SecurityKeyProvider=/usr/lib/ssh-keychain.dylib
  • Khi chạy ssh <server nickname>, macOS sẽ tự động hiển thị yêu cầu xác thực vân tay trước khi đăng nhập, rồi đăng nhập SSH diễn ra bình thường

Ký commit git bằng secure element của MacBook

  • Ngay cả khi đặt git config --global user.signingKey /Users/ahelwer/.ssh/id_ecdsa_sk_rk và cập nhật tệp .ssh/allowed_signers, việc ký commit git vẫn không hoạt động ngay
  • git sẽ báo lỗi khi ký commit, như device not found?
error: Signing file /var/folders/l5/5wqvq2l10p96wtdtfr6lvrvw0000gn/T//.git_signing_buffer_tmpc4uQgO
Confirm user presence for key ECDSA-SK SHA256:oQDA2SNYb2MoSQcxJVSmWyAeAWPqMp7rxliBRfi87as
Couldn't sign message: device not found?
Signing /var/folders/l5/5wqvq2l10p96wtdtfr6lvrvw0000gn/T//.git_signing_buffer_tmpc4uQgO failed: device not found?

fatal: failed to write commit object
  • Giải pháp là dùng ssh-agent thay vì trỏ trực tiếp tới tệp trong thư mục ~/.ssh
  • Theo tutorial ở trên, hãy đăng ký cặp khóa vào ssh-agent bằng lệnh sau
ssh-add -K -S /usr/lib/ssh-keychain.dylib
  • Sau đó, trong user.signingKey, không dùng đường dẫn tệp mà đặt chính khóa với tiền tố key:: trước nội dung của ~/.ssh/id_ecdsa_sk_rk.pub vào ~/.gitconfig
[user]
	name = Andrew Helwer
	signingKey = "key::sk-ecdsa-sha2-nistp256@openssh.com AAAAInNrLWVjZHNhLXNoYTItbmlzdHAyNTZAb3BlbnNzaC5jb20AAAAIbmlzdHAyNTYAAABBBGxFEdnIg6ppz+pQCdd1eisjOV4gxrjMv1Y4SbtdLoSm6CJCgPZ6q7lnNyuQQsdnS4/Tllsc656AQL7BO3OS47cAAAAEc3NoOg== ssh:"
  • Sau cấu hình này, tác giả có thể ký tệp bằng khóa nằm trong secure element của MacBook và push lên trang GitLab Pages

Kết quả thử nghiệm trên Windows và Linux

  • Tác giả cũng thử nhanh trên laptop Windows do công ty cấp
winget install Microsoft.OpenSSH.preview
ssh-keygen -t ecdsa-sk
  • Lệnh này cũng tạo ra cặp tệp khóa riêng tư/công khai, và khi kết nối SSH thì chấp nhận một trong các luồng đăng nhập tiêu chuẩn của Windows Hello: nhận diện khuôn mặt, vân tay hoặc PIN
  • Trên Linux, tác giả không thể demo vì không có quyền truy cập vào chiếc laptop có secure element sau bước xác nhận sự hiện diện thực tế của người dùng tương tự như trên

1 bình luận

 
Ý kiến trên Lobste.rs
  • Bài viết rất hay, và chỉ riêng việc cho thấy điều này là khả thi đã cực kỳ hữu ích
    Cá nhân tôi thì chưa tìm ra đúng phiên bản thư viện để làm nó chạy, nhưng tôi biết được rằng 1Password 8 lưu khóa SSH an toàn và agent hỗ trợ mở khóa bằng xác thực sinh trắc học
    Vì vậy giờ tôi có thể làm việc với git và đăng nhập vào host SSH chỉ bằng cách đặt ngón tay lên cảm biến
    Hướng dẫn: https://developer.1password.com/docs/ssh/get-started/

  • Cái này có vẻ chỉ dành cho Mac

    • Tôi có thể dùng laptop Windows mới ở công ty, nên đã cấu hình để OpenSSH tích hợp với Windows Hello như sau
      winget install Microsoft.OpenSSH.preview  
      ssh-keygen -t ecdsa-sk  
      
      Sau đó nó hoạt động như trước, và mỗi lần SSH đến đâu đó bằng khóa, tôi có thể dùng một trong các luồng Windows Hello tiêu chuẩn: cảm biến vân tay, nhận diện khuôn mặt hoặc PIN
      Tôi chưa có dịp thử hệ thống Linux nào có phần tử bảo mật như vậy, và máy trạm Linux của tôi có TPM V1 nhưng tôi không rõ có cách nào đảm bảo thao tác ký chỉ được thực hiện sau khi xác nhận sự hiện diện thực sự của người dùng hay không
      Có lẽ ai dùng laptop Linux như Framework có thể thử. Biết đâu nó cũng thật sự chạy được trên Asahi
    • Có thể xem là gần như vậy. Tôi không rõ trên Linux hay Windows có lớp giả lập TPM2 FIDO hay không
  • Vậy chính xác thì trong file private key được cung cấp có những gì?

    • @wrs đã trả lời trước rồi, nhưng phần ssh:, tức application, tương ứng với origin của passkey và hữu ích khi tạo resident key theo từng host hoặc domain
      Ví dụ, tôi dùng nó để tách khóa theo từng mục đích ngay cả trên cùng một Yubikey vật lý
      flags chỉ định phần cứng phải xử lý khóa như thế nào [1]. Agent cũng có thể tự thêm các ràng buộc riêng
      Về mặt kỹ thuật, bạn cũng có thể lưu các blob hoặc extension khác trong khóa FIDO, và ở công việc trước đây tôi từng dùng nó để chuyển các thông tin xác thực phụ như khóa công khai X.509 cùng với quá trình xác thực. Cách này khá hay
      [1]
      #define SSH_SK_USER_PRESENCE_REQD  0x01  
      #define SSH_SK_USER_VERIFICATION_REQD  0x04  
      #define SSH_SK_FORCE_OPERATION    0x10  
      #define SSH_SK_RESIDENT_KEY    0x20  
      
    • Theo Claude, và khi kiểm tra bằng openssh_key_parser, cấu trúc như sau
      Phần bao ngoài có magic value openssh-key-v1\0, cipher=none, kdf=none, nên nó không phải bản mã
      Blob public key dài 74 byte chứa kiểu khóa sk-ssh-ed25519@openssh.com, điểm Ed25519 32 byte fdcce889…03e7852b, và application ssh:; giá trị này dùng để tách namespace thông tin xác thực FIDO giữa SSH và WebAuthn
      Phần private dài 248 byte và vì cipher=none nên ở dạng plain text. Nó chứa giá trị ngẫu nhiên checkint1 == checkint2 == 0x46744267, kiểu khóa và public key lặp lại, application ssh:, và flags: 0x01
      Cờ này là USER_PRESENCE_REQUIRED, nên cần chạm nhưng không yêu cầu PIN/xác minh người dùng, và đây là khóa không thường trú
      key_handle là credential ID mờ đục dài 128 byte được truyền vào authenticatorGetAssertion, và thiết bị sẽ giải nó nội bộ để khôi phục seed Ed25519
      Ngoài ra còn có reserved rỗng, comment ahelwer@ah-mbair.local, và padding 01 02 03
    • Nếu đưa vào bộ giải mã base64 thì sẽ ra như sau

      openssh-key-v1����none���none����������J���sk-ssh-ed25519@openssh.com��� 盘˪<F$KW+���ssh:���FtBgFtBg���sk-ssh-ed25519@openssh.com��� 盘˪<F$KW+���ssh:���fІpF$D8"&0[X 'L=Ev ')BjM]$}rTv6Z+p9O8ݹ%V* f.|қ.%I{9 .W !{"8N ai*W�y53 �������ahelwer@ah-mbair.local
      Trong đó có phiên bản chuẩn khóa v1, kiểu khóa sk-ssh-ed25519@openssh.com được lặp lại vì một lý do nào đó, và cả tên khóa dễ đọc với con người là ahelwer@ah-mbair.local
      Phần còn lại có lẽ là các cờ OpenSSH, ví dụ có yêu cầu PIN hay xác nhận sự hiện diện của người dùng hay không, cùng với handle GUID để OpenSSH gửi qua API FIDO/U2F kèm challenge
      OpenSSH có thể suy ra từ kiểu khóa, đặc biệt là sk, rằng đây không phải private key thật mà cần gọi đến phần tử bảo mật
      Sau đó nó sẽ kiểm tra cấu hình SecurityKeyProvider hoặc biến môi trường SSH_SK_PROVIDER để biết phải nạp thư viện động ở đâu nhằm giao tiếp với phần tử bảo mật đó

  • Bài này có vẻ chỉ nói về SSH, nhưng có cách nào dùng Secure Enclave hoặc TPM trên máy tính của tôi như khóa FIDO2 hoặc U2F không?

    • Tất nhiên là được, và gần như nên hoạt động chỉ với cấu hình mặc định
      Passkey cũng là một dạng của cách làm này, dùng private key riêng biệt cho từng website
  • Nhìn vào điều này, tôi thấy khá lạ là hỗ trợ cho API key bất đối xứng hoặc HMAC có thể ràng buộc với phần cứng lại không phổ biến hơn
    Thật đáng mừng khi ngày càng có nhiều đặc tả thúc đẩy hướng này hơn, như WebAuthn, DBSC(Device-Bound Session Credentials), và OAuth2 DPOP