# 요즘 노트북은 모두 내장 보안 토큰을 갖고 있다

> Clean Markdown view of GeekNews topic #29359. Use the original source for factual precision when an external source URL is present.

## Metadata

- GeekNews HTML: [https://news.hada.io/topic?id=29359](https://news.hada.io/topic?id=29359)
- GeekNews Markdown: [https://news.hada.io/topic/29359.md](https://news.hada.io/topic/29359.md)
- Type: GN+
- Author: [neo](https://news.hada.io/@neo)
- Published: 2026-05-10T20:03:19+09:00
- Updated: 2026-05-10T20:03:19+09:00
- Original source: [ahelwer.ca](https://ahelwer.ca/post/2026-05-08-builtin-u2f/)
- Points: 1
- Comments: 1

## Topic Body

- **보안 토큰**은 개인키를 장치 밖으로 내보내지 않고 장치 안에서 서명하며, 사용자의 물리적 동작을 요구해 원격 공격자가 임의 서명을 만들기 어렵게 함
- SSH 인증, U2F, 비밀번호 없는 로컬 로그인, `sudo`, git 커밋 서명에 쓸 수 있고, 최신 노트북·스마트폰의 내장 보안 장치가 **YubiKey**를 대체할 수 있음
- `ssh-keygen -t ed25519-sk`로 만든 “개인키” 파일은 실제 개인키가 아니라 토큰 안 키를 가리키는 **핸들**이며, 같은 토큰으로 다른 컴퓨터에서도 같은 SSH 키 파일 생성 가능함
- MacBook에서는 **secure element**를 SSH 키로 설정해 Touch ID 기반 SSH 로그인이 가능했고, git 커밋 서명에는 파일 경로 대신 `ssh-agent`와 `key::` 형식의 `user.signingKey` 설정이 필요했음
- 보안 토큰은 분실 시 개인키를 복구할 수 없고 반복 터치에 익숙해지는 사용성 위험이 있으며, Windows 노트북에서는 **Windows Hello**가 얼굴 인식·지문·PIN으로 SSH 키 사용을 확인할 수 있었음

---

### 보안 토큰의 장점과 한계
- ## 원격 공격을 막는 핵심 구조
  - 보안 토큰은 개인키/공개키 쌍을 장치 안에 두고, 공개키는 쉽게 꺼낼 수 있지만 **개인키는 장치 밖으로 나가지 않게** 만드는 장치임
  - 서명할 데이터 패킷을 장치에 보내면 개인키로 장치 안에서 서명되며, 보통 깜박이는 터치 버튼을 누르는 같은 **물리적 사용자 동작**이 필요함
  - 원격 공격자가 컴퓨터에 접근해도 사용자가 실제 세계에서 동작하지 않으면 보안 토큰이 임의의 서명을 해주지 않으므로, `~/.ssh` 디렉터리에 전체 SSH 개인키/공개키 쌍을 파일로 두는 방식보다 나아 보임
  - [SoloKeys](https://solokeys.com/)와 [Nitrokeys](https://www.nitrokey.com/)처럼 FOSS 펌웨어를 원하는 선택지도 있음
  - 더 고급형 보안 토큰은 내장 지문 리더 같은 생체 인증 기능을 더하지만, 핵심은 개인키를 장치 밖으로 내보내지 않는 구조임
- ## 사용성에서 생기는 위험
  - 사용자가 보안 토큰이 깜박일 때마다 누르는 데 익숙해지면, 악의적인 요청에도 무심코 응답할 수 있음
  - 연속 서명 작업 중 토큰을 반복해서 누르는 상황에서는 한 번 더 깜박이는 요청을 실제로 알아차리기 어려울 수 있음
  - Apple과 Microsoft는 스마트폰의 인증 앱에서 접근 요청마다 무작위 숫자 코드를 붙여 사용자가 입력하게 하는 방식을 쓰지만, 이는 번거롭고 [TOTP](https://en.wikipedia.org/wiki/Time-based_one-time_password)를 쓰는 Authy나 Google Authenticator 앱 대비 보안 토큰의 사용성 이점을 줄임
- ## 분실과 백업 문제
  - 보안 토큰을 잃어버리면 해당 개인키는 영구히 사라지고 백업할 방법이 없음
  - 여러 계정에서 잠기는 위험을 피하려면 보안 토큰을 살 때 최소 2개를 사서 같은 서비스에 등록해야 함
  - 대안으로 [BIP 39](https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki)처럼 개인키를 사람이 읽을 수 있는 단어 목록으로 바꿔 적어두는 백업·복원 방식이 있음
  - 개인키가 보안 엔클레이브 밖으로 나갈 수 있으면 사용자가 단어 목록을 잘못된 곳에 쓰도록 유도하는 피싱 공격도 가능해짐
  - 모든 보안 토큰을 잃을 가능성이 크게 걱정된다면 BIP 39 단어 목록이 시스템 접근을 되찾기 위한 최후 수단이 될 수 있음

### SSH와 git에서 보안 토큰 쓰기
- ## SSH 개인키를 보안 토큰에 두기
  - 일반적으로 `ssh-keygen`을 실행하면 전체 개인키가 포함된 파일 쌍이 만들어짐
  - 보안 토큰에 개인키를 두려면 Yubico의 [FIDO/U2F 지침](https://developers.yubico.com/SSH/Securing_SSH_with_FIDO2.html)에 따라 [libfido2](https://github.com/yubico/libfido2)를 설치하고, 보안 토큰을 꽂은 상태에서 `ssh-keygen -t ed25519-sk`를 실행함
  - 이때도 파일 쌍이 생성되지만, “개인키” 파일은 실제 개인키가 아니라 보안 토큰 안에 있는 개인키를 가리키는 핸들임
  - 같은 보안 토큰으로 `ssh-keygen -t ed25519-sk`를 다시 실행하면 어느 컴퓨터에서든 같은 개인키/공개키 파일을 만들 수 있어, SSH 접근 권한이 특정 컴퓨터의 특정 파일이 아니라 보안 토큰과 함께 이동함
- ## git 인증과 커밋 서명
  - 보안 토큰을 누르는 상황의 약 90%는 git 사용 때문임
  - git forge들은 push와 pull 작업을 위한 SSH 인증을 구현하고, 위에서 생성한 `id_ed25519_sk.pub` 파일을 올리면 보안 토큰 키 쌍을 허용할 수 있음
  - git은 커밋 서명에도 SSH 키를 지원하며, GitHub 문서의 [SSH 키로 서명 키 설정하기](https://docs.github.com/en/authentication/managing-commit-signature-verification/telling-git-about-your-signing-key#telling-git-about-your-ssh-key)를 따른 뒤 `git config --global commit.gpgsign true`를 실행하면 모든 커밋이 자동 서명됨
  - git forge가 커밋을 본인 서명으로 인식하려면 공개키를 다시 업로드해야 하며, 이 필드는 보통 SSH 인증용 필드와 별도임
- ## 커밋 서명의 불편함
  - 긴 커밋 목록을 rebase할 때는 모든 커밋을 다시 서명해야 함
  - 지문 리더가 있는 YubiKey는 수십 개 커밋을 연속 서명하기에는 지문 인식 실패율이 너무 높아 사용을 중단하게 됨
  - git의 “rebase/amend 중심” 래퍼인 jujutsu에는 [push 시점에만 커밋에 서명](https://lobste.rs/s/ha3xyn/git_stage_over_git_add#c_qvr6p7)하는 방법이 있음
- ## Linux 로컬 로그인과 sudo
  - Linux 시스템에서는 [Pluggable Authentication Module(PAM)](https://wiki.archlinux.org/title/YubiKey#Linux_user_authentication_with_PAM)으로 보안 토큰을 비밀번호 없는 로컬 로그인과 `sudo` 권한 상승에 사용함

### MacBook의 secure element를 SSH 키로 쓰기
- USB-C 포트에 보안 토큰을 계속 꽂아두면 잘못 떨어뜨리거나 부딪혔을 때 포트와 토큰을 망가뜨릴 수 있는 작은 지렛대처럼 튀어나와 있음
- 2020년형 M1 MacBook Air에서 Arian van Putten의 [지침](https://gist.github.com/arianvp/5f59f1783e3eaf1a2d4cd8e952bb4acf)을 따라 내장 보안 요소를 SSH 키로 설정함

```sh
sc_auth create-ctk-identity -l ssh -k p-256-ne -t bio
ssh-keygen -w /usr/lib/ssh-keychain.dylib -K -N ""
```

- 이 명령은 `id_ecdsa_sk_rk` 개인키/공개키 파일 쌍을 만들고, 이 파일들을 `~/.ssh` 디렉터리로 옮김
- 여기서도 개인키 파일은 실제 개인키가 아니라 장치 안 키에 대한 핸들이므로 공개적으로 붙여 넣을 수 있는 형태임
- 홈랩 서버에 공개키를 authorized key로 추가하려면 다음처럼 실행함

```sh
ssh-copy-id -i ~/.ssh/id_ecdsa_sk_rk.pub &lt;server nickname&gt;
```

- 이후 `~/.ssh/config`에 다음 설정을 추가함

```sh
Host *
  IdentityFile ~/.ssh/id_ecdsa_sk_rk
  SecurityKeyProvider=/usr/lib/ssh-keychain.dylib
```

- `ssh &lt;server nickname&gt;`을 실행하면 로그인 전에 macOS가 지문 요청을 자동으로 띄우고, 이후 SSH 로그인이 정상적으로 진행됨

### MacBook secure element로 git 커밋 서명하기
- `git config --global user.signingKey /Users/ahelwer/.ssh/id_ecdsa_sk_rk`를 설정하고 `.ssh/allowed_signers` 파일을 갱신해도 git 커밋 서명은 바로 동작하지 않음
- git은 커밋 서명에 실패하며, `device not found?` 같은 오류를 출력함

```sh
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
```

- 해결책은 `~/.ssh` 디렉터리의 파일을 직접 가리키는 대신 **ssh-agent**를 쓰는 것임
- 위의 [튜토리얼](https://gist.github.com/arianvp/5f59f1783e3eaf1a2d4cd8e952bb4acf)에 따라 다음 명령으로 키 쌍을 ssh-agent에 등록함

```sh
ssh-add -K -S /usr/lib/ssh-keychain.dylib
```

- 이후 `user.signingKey`에는 파일 경로가 아니라 `~/.ssh/id_ecdsa_sk_rk.pub` 내용 앞에 `key::`를 붙인 키 자체를 `~/.gitconfig`에 넣음

```sh
[user]
	name = Andrew Helwer
	signingKey = "key::sk-ecdsa-sha2-nistp256@openssh.com AAAAInNrLWVjZHNhLXNoYTItbmlzdHAyNTZAb3BlbnNzaC5jb20AAAAIbmlzdHAyNTYAAABBBGxFEdnIg6ppz+pQCdd1eisjOV4gxrjMv1Y4SbtdLoSm6CJCgPZ6q7lnNyuQQsdnS4/Tllsc656AQL7BO3OS47cAAAAEc3NoOg== ssh:"
```

- 이 설정 뒤 MacBook의 secure element에 있는 키로 파일을 서명하고 GitLab Pages 사이트에 push할 수 있었음

### Windows와 Linux에서 해본 결과
- 회사 지급 Windows 노트북에서도 빠른 실험을 진행함

```sh
winget install Microsoft.OpenSSH.preview
ssh-keygen -t ecdsa-sk
```

- 이 명령도 개인키/공개키 파일 쌍을 생성했고, SSH 접속 시 Windows Hello의 표준 로그인 흐름을 통해 얼굴 인식, 지문, PIN 중 하나를 받아들였음
- Linux에서는 비슷한 실제 사용자 존재 확인 뒤에 secure element가 걸려 있는 노트북에 접근할 수 없어 데모하지 못함

## Comments



### Comment 57173

- Author: neo
- Created: 2026-05-10T20:03:19+09:00
- Points: 1

###### [Lobste.rs 의견들](https://lobste.rs/s/ebg5hg/laptops_all_have_built_security_tokens) 
- 훌륭한 글이고, 이런 게 **가능하다는 사실**을 드러내는 것만으로도 매우 유익함  
  개인적으로는 이걸 동작시키는 데 맞는 라이브러리 버전을 찾지 못했지만, 1Password 8은 SSH 키를 안전하게 저장하고 에이전트가 **생체 인증으로 키 잠금 해제**를 지원한다는 걸 알게 됨  
  그래서 이제 손가락을 대는 것만으로 git 작업도 하고 SSH 호스트에도 로그인할 수 있음  
  안내: https://developer.1password.com/docs/ssh/get-started/
  - Bitwarden도 비슷하게 지원함: https://bitwarden.com/help/ssh-agent/

- 이건 **Mac 전용**처럼 보임
  - 회사에서 최신 Windows 노트북을 쓸 수 있어서, 다음처럼 OpenSSH가 **Windows Hello**와 연동되게 했음  
    ```sh  
    winget install Microsoft.OpenSSH.preview  
    ssh-keygen -t ecdsa-sk  
    ```  
    이후에는 예전처럼 동작했고, 키로 어디든 SSH 접속할 때마다 표준 Windows Hello 흐름을 거쳐 지문 인식기, 얼굴 인식, PIN 중 하나를 사용할 수 있었음  
    그런 보안 요소가 있는 Linux 시스템은 써볼 수 없었고, 내 Linux 워크스테이션에는 V1 TPM이 있지만 서명 작업을 실제 사용자 존재 확인 뒤에만 실행되게 보장하는 방법은 딱히 모르겠음  
    Framework 같은 Linux 노트북을 가진 사람이 시도해볼 수 있을지도 모름. 어쩌면 Asahi에서도 실제로 동작할 수 있음
  - 거의 그렇다고 봄. Linux나 Windows에서 **TPM2 FIDO 에뮬레이션 계층**이 있는지는 잘 모르겠음

- 그래서 제공된 **private key 파일** 안에는 정확히 뭐가 들어 있는 거임?
  - @wrs가 먼저 답했지만, `ssh:` 부분, 즉 애플리케이션은 passkey의 origin에 해당하며 호스트나 도메인별 **resident key**를 만들 때 유용함  
    예를 들어 같은 물리 Yubikey 안에서도 용도별로 키를 분리하는 데 쓰고 있음  
    `flags`는 하드웨어가 키를 어떻게 다뤄야 하는지 지정함 [1]. 에이전트가 자체 제약을 덧붙일 수도 있음  
    기술적으로는 FIDO 키에 다른 블롭이나 확장도 저장할 수 있고, 이전 직장에서는 인증과 함께 X.509 공개 키 같은 보조 자격 증명을 넘기는 데 써본 적이 있음. 꽤 멋진 방식임  
    [1]  
    ```c  
    #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  
    ```
  - Claude에 따르면, 그리고 openssh_key_parser로 검증해보면 구조는 다음과 같음  
    바깥 래퍼에는 매직값 `openssh-key-v1\0`, `cipher=none`, `kdf=none`이 있어서 암호문이 아님  
    공개 키 블롭 74바이트에는 키 타입 `sk-ssh-ed25519@openssh.com`, 32바이트 Ed25519 점 `fdcce889…03e7852b`, 애플리케이션 `ssh:`가 들어 있으며, 이 값이 SSH와 WebAuthn의 FIDO 자격 증명을 네임스페이스로 분리함  
    개인 섹션은 248바이트이고 `cipher=none`이라 평문임. `checkint1 == checkint2 == 0x46744267` 랜덤값, 반복된 키 타입과 공개 키, 애플리케이션 `ssh:`, `flags: 0x01`이 들어 있음  
    이 플래그는 `USER_PRESENCE_REQUIRED`라 터치가 필요하지만 PIN/사용자 검증은 없고, 비상주 키임  
    `key_handle`은 128바이트짜리 불투명 자격 증명 ID로 `authenticatorGetAssertion`에 전달되고, 장치가 내부적으로 풀어서 Ed25519 시드를 복구함  
    그 밖에 빈 `reserved`, 주석 `ahelwer@ah-mbair.local`, 패딩 `01 02 03`이 있음
  - base64 디코더에 넣으면 다음이 나옴  
    >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+p9O8ݹ%V*
f.|қ.%I{9 .W  !D"8N
ai*W�y53 �������ahelwer@ah-mbair.local  
    여기에는 키 표준 버전 v1, 키 타입 `sk-ssh-ed25519@openssh.com`이 들어 있고 어떤 이유로 반복되며, 사람이 알아보기 쉬운 키 이름 `ahelwer@ah-mbair.local`도 있음  
    나머지는 OpenSSH 플래그, 예를 들면 PIN이나 사용자 존재 확인 필요 여부, 그리고 OpenSSH가 챌린지와 함께 FIDO/U2F API로 보낼 수 있는 핸들 GUID일 것 같음  
    OpenSSH는 키 타입, 특히 `sk`를 보고 이게 실제 개인 키가 아니라 **보안 요소**를 호출해야 한다고 추론할 수 있음  
    그다음에는 보안 요소와 통신하게 해주는 동적 라이브러리를 어디서 로드할지 `SecurityKeyProvider` 설정이나 `SSH_SK_PROVIDER` 환경 변수를 확인함

- 이 글은 SSH만 다루는 것 같은데, 내 컴퓨터의 **Secure Enclave**나 TPM을 FIDO2 또는 U2F 키로 쓸 방법이 있을까?
  - 당연히 가능하고, 거의 기본 설정만으로 동작해야 함  
    **Passkey**도 웹사이트마다 별도의 개인 키를 쓰는 이런 방식의 한 형태임

- 이 사실을 보면, 하드웨어에 묶을 수 있는 **비대칭 또는 HMAC API 키** 지원이 더 흔하지 않다는 게 이상하게 느껴짐  
  WebAuthn, DBSC(Device-Bound Session Credentials), OAuth2 DPOP처럼 이런 방향을 더 밀어주는 명세가 늘어나는 건 반가움
