작은 배포가 지문으로 눈을 돌리는 이유는 인증 기관이 아무도 운영할 시간이 없는 인프라처럼 보이기 때문이다. 그 직감은 합리적이지만, 사람들이 대신 택하는 지름길—데이터 채널의 비밀 하나를 양쪽 끝에 복사하는 것—은 그렇지 않다. peer-fingerprint 구성은 TLS 구조를 그대로 유지한다 — 각 측은 여전히 자기 개인 키와 인증서를 보유하고, 각 측은 여전히 상대를 검증한다. 달라지는 것은 검증이 어디에 고정되는가이며, 신뢰할 수 있는 발급자에서 고정된 다이제스트의 짧은 목록으로 옮겨 간다. 다이제스트는 이름이 아니라 인증서 전체를 덮기 때문에, 갱신은 실제로 시간을 쓰는 작업이 된다.

지문이 실제로 약속하는 것

인증서 지문은 DER로 인코딩된 인증서 전체의 다이제스트이지, 공개 키의 것도 아니고 주체 이름의 것도 아니다. 그래서 이름이 같고 키도 같지만 유효 기간이 다른 두 인증서는 서로 다른 지문을 가진다. 이것이 고정을 정밀하게 만들고, 동시에 갱신을 번거롭게 만든다. 고정은 키 전반에 대한 신뢰를 뜻할 수 없다 — 그것은 한 산출물을 그 생애의 한 시점에서 지목하기 때문이다. 다이제스트는 누구나 알 수 있고, 아는 것 자체로는 아무것도 증명하지 않는다.

openssl x509 -in server.crt -noout -fingerprint -sha256
openssl x509 -in client-07.crt -noout -fingerprint -sha256

대응하는 개인 키를 보유하는 것이 피어가 그 인증서를 가졌음을 증명하며, 다이제스트는 상대가 애초에 그 인증서를 기대했는지 판단하는 수단이다. 이 분리가 바로 이 설계가 옛 공유 비밀 방식이 아닌 이유다 — 공유 키는 같은 바이트를 아는 누구든 인증하지만, 고정은 하나의 인증서를 인증한다. 다른 장치는 같은 비밀의 사본이 아니라 자기 항목을 얻는다 — 이는 파일에서는 작은 차이이고 피해 범위에서는 큰 차이다.

지도는 상호적이며 대칭이 아니다

각 끝점에는 자기 개인 키와 인증서, 그리고 어떤 피어 인증서를 받아들일지에 대한 진술, 이렇게 두 가지가 필요하다. 클라이언트에서 그 진술은 서버 인증서의 다이제스트이고, 서버에서는 등록된 클라이언트 다이제스트의 목록이다. Community 2.6용 소규모 구성의 공식 예시가 peer-fingerprint와 함께 배치를 보여 주며, 실제 배포에 이 신뢰 모델을 고르기 전에 읽어 볼 만하다. 두 방향은 하나의 목록을 뒤집은 것이 아니고, 같은 순간에 실패하지도 않는다.

Verification directionWhat is pinnedWhat never leaves that endpointHow a wrong pin shows up
Client checks the serverOne digest, for the server certificateThe server private keyThe client aborts during the TLS handshake
Server checks the clientA digest per enrolled clientEach client private keyThe server rejects the peer before a tunnel exists

장치를 추가하는 일은 서버의 허용 목록을 편집하는 것이며, 기존 클라이언트의 개인 자료를 복사하지도 않고 서버 키를 어디로 옮기지도 않는다. 두 방향은 독립적으로도 실패하므로 진단에 유용하다 — 핸드셰이크를 닫는 쪽이 기대가 충족되지 않은 쪽이다. 클라이언트가 서버를 거부하면 다이제스트가 제시된 인증서와 맞지 않는다. 서버가 클라이언트를 거부하면 그 클라이언트의 다이제스트가 목록에 없거나, 클라이언트가 등록된 것과 다른 인증서를 제시하고 있다.

등록: 새 장치는 어떻게 합류하는가

새 장치의 키와 인증서는 개인 키가 머무를 곳에서 생성한 뒤, 공개 산출물만 고정해야 하는 쪽으로 옮긴다. 예상 다이제스트는 이미 신뢰하는 경로로 전달하고, 낯익은 이름 대신 인증서 전체를 비교한다. 인증서와 같은 미검증 다운로드에서 복사한 다이제스트는 두 파일이 서로 일치한다는 것만 증명한다. 이는 동일성 검사가 아니라 일관성 검사이며, 이 설계가 가장 조용히 약해지는 경로다.

서버에서 다이제스트를 추가하는 것은 일반 관리자를 거치는 구성 변경이며 그 뒤에 재연결이 따른다. 새 장치가 연결되고 다이제스트가 제거된 장치는 더 이상 연결되지 않음을 확인한다. 긍정 사례만 다루는 시험은 허용 목록을 검증되지 않은 채로 남기며, 반년 뒤 당신이 처하게 될 상태가 바로 그것이다.

갱신이 비싼 순간이다

모든 인증서는 만료되고, 키와 이름이 그대로여도 새 인증서는 새 다이제스트를 가진다. 발급자 아래에서 갱신은 그 발급자를 신뢰하는 피어에게 투명하다 — 고정 아래에서는 인증서를 고정하는 모든 피어에게 등록 사건이 된다.

만료일 이후가 아니라 그 이전에 순서를 계획한다. 새 다이제스트를 검증하는 측에 배포하고, 변경을 적용하고, 그다음에야 끝점의 인증서를 교체한다. 그 순서라면 어느 인증서가 현재인지 두 측이 엇갈리는 창이 생기지 않는다. 목록 자체도 검토 아래 두어야 한다 — 고정된 목록에는 몇 달 전에 퇴역한 장치의 항목이 쌓이기 때문이다. 오래된 항목은 모두 그 키가 살아남으면 여전히 인증되는 정체성이며, 하나를 지우는 일은 정리가 아니라 보안 조치다.

신뢰 지도가 결코 정하지 않는 것

인증은 한 계층이며, 고정된 지도는 주소 풀, 밀어 넣는 경로, 경로 MTU, 방화벽 정책에 대해 아무것도 말하지 않는다. 그것들은 별도의 구성과 별도의 실패 방식으로 남는다. 인증은 되지만 트래픽이 전혀 지나가지 않는 터널은 결코 올라오지 않는 터널과 다른 조사 대상이다.

지도 역시 무한히 확장되지는 않으며, 더 큰 배포는 발급자 기반 PKI로 귀결된다. 그 선호는 암호가 아니라 운영에 관한 것이다 — 함대가 커질수록 모든 피어에서 목록을 편집하는 것보다 하나의 기관을 통해 정체성을 추가하고 퇴역시키는 편이 저렴하기 때문이다. 클라이언트 구현이 그 옵션을 지원하지 않는 경우, 답은 그 클라이언트가 문서화하는 호환 신뢰 모델이거나 지원되는 대체물이다. 검증 경고를 억제하는 것은 그 지원을 더하지 않으며, 이 설계의 가치는 검증이 여전히 일어난다는 데 있다.

관련 글