ACME: 인증서 발급이 API 호출이 된 과정

TLS 인증서를 받는 건 이메일, 서류, CSR을 웹 폼에 붙여넣는 일이었다. ACME는 그 전부를 새벽 3시에 스크립트가 돌리는 프로토콜로 바꿨다. 그 원리 — 그리고 우리가 맞바꾼 것.

옛날 방식으로 인증서를 마지막으로 산 날, 그 의식은 이랬다. 서버에서 키와 인증서 서명 요청(CSR)을 생성한다, OpenSSL 명령을 손가락으로 잘못 치지 않도록 조심하면서. CSR을 웹 폼에 붙여넣는다. 매직 주소 — 도메인의 admin@이나 승인된 목록의 주소 — 로 온 이메일을 받아, 그 편지함을 읽을 수 있다는 걸 증명하러 링크를 클릭한다. 기다린다. 인증서와, 운이 좋으면 올바른 중간 체인이 든 zip을 내려받는다. 체인 순서가 어느 쪽인지 알아낸다. 사이트를 죽이지 않고 설치한다. 1년 뒤로 캘린더 알림을 걸어두고, 그건 무시할 거고, 그러면 인증서는 토요일에 만료된다.

그 목록의 모든 단계는 기계가 할 수 있었던 일을 사람이 하는 것이었다. ACME는 그걸 알아챈 프로토콜이다.

인증서를 공짜로 만든 공을 Let’s Encrypt에 돌리기는 쉽고, 실제로 그랬지만, “공짜”는 덜 흥미로운 절반이다. 흥미로운 절반은, 인증서를 받는 일이 사람과 회사 사이의 거래가 아니라 두 프로그램 사이의 대화가 됐다는 것이다. IETF는 그 대화를 2019년 RFC 8555로 표준화했다, Let’s Encrypt가 첫 버전을 내놓은 2015년으로부터 몇 년 뒤에. 오늘날 인증서 갱신은 아무도 지켜보지 않는 cron 작업이고, 아무도 지켜보지 않는다는 그 사실이 핵심 전부다.

기계가 실제로 증명하는 것

도구를 걷어내면 인증서는 작고 시시한 주장 하나다: 이 키의 소유자가 이 도메인 이름을 통제한다. 그게 다다. Domain Validation 인증서는 그보다 거창한 걸 주장한 적이 없다 — 당신이 실재하는 사업체라는 것도, 정직하다는 것도 아니고, 발급 시점에 그 이름의 통제를 보일 수 있었다는 것뿐이다. 옛 이메일 춤은 그 주장을 확인하는 어설픈 방법이었고, 이미 무너지고 있던 가정에 기대고 있었다: admin@에서 사람이 메일을 읽는다는 가정.

ACME의 통찰은, “도메인을 통제한다”는 것이 프로그램으로 증명 가능해야 한다는 것이다. 실제로 도메인을 통제하는 그것이 프로그램이기 때문이다 — 80번 포트에서 응답하는 웹 서버, 또는 질의에 답하는 DNS 존. 그래서 프로토콜은 소프트웨어가 할 수 있는 방식으로 통제를 증명하라고 한다: 통제하는 자만 바꿀 수 있는 무언가를 바꾸고, CA가 가서 보게 하라.

얼개는 이렇다. 클라이언트가 계정 키 쌍을 생성해 CA에 등록한다; 그때부터 모든 요청은 그 키로 서명되므로, CA는 처음부터 끝까지 같은 상대와 대화하고 있음을 안다. 재전송(replay)은 서버가 건네주고 다음 요청에서 되돌려받길 요구하는 nonce로 막힌다. 도메인 이름 집합에 대해 *주문(order)*을 넣는다. 각 이름마다 CA는 하나 이상의 챌린지가 담긴 *인증(authorization)*을 돌려준다 — 하나를 골라 충족하고, CA에게 확인하라고 한다. 모든 인증이 유효해지면 CSR을 보내고, CA가 발급하고, 체인을 내려받는다. 편지함도, zip 파일도, 첫 설정 이후엔 루프 안의 사람도 없다.

통제를 증명하는 세 가지 방법

챌린지는 설계가 의견을 드러내는 지점이고, 알아둘 가치가 있다. 잘못 고르는 것이 사람들이 겪는 고통의 대부분이기 때문이다.

http-01은 기본값이고 누구나 처음 만나는 것이다. CA가 클라이언트에 토큰을 주면, 클라이언트는 특정 문자열 — 토큰에 계정 키의 해시를 이은 것 — 을 http://도메인/.well-known/acme-challenge/<토큰>에, 80번 포트, 평문 HTTP로 서빙한다. CA가 그 URL을 가져와 내용을 확인한다. 그 이름의 80번 포트에서 응답하는 것을 당신이 통제함을 증명한다. 와일드카드는 발급 못 한다. www.example.com의 웹 루트를 통제한다고 존 전체에 대해 말하는 게 아니기 때문이다. 그리고 80번 포트가 바깥에서 닿아야 하는데, 일부 방화벽과 로드밸런서 뒤에선 이게 진짜 제약이다.

dns-01은 웹 경로가 아니라 DNS 존의 통제를 증명한다. _acme-challenge.도메인에 키 인증의 해시를 값으로 하는 TXT 레코드를 게시하면 CA가 그걸 해석한다. 와일드카드 인증서를 발급할 수 있는 유일한 챌린지다. 존에 쓸 수 있음을 증명하는 것이 그 아래 모든 이름의 통제를 증명하기 때문이다. 함정은 실재하고 소리 내어 말할 가치가 있다: dns-01을 자동화한다는 건 ACME 클라이언트에게 DNS 레코드를 쓸 자격증명을 넘긴다는 뜻이다. 많은 와일드카드 구성이 갱신 스크립트에 존을 다시 쓸 수 있는 API 토큰을 조용히 부여한다. 그 토큰은 이제 당신 공격 표면의 일부이며, 인증서를 갱신하는 바로 그 머신에 놓여 있다.

tls-alpn-01은 전문가용이고, RFC 8737로 따로 표준화됐다. CA가 443번 포트에 접속해 특수 ALPN 프로토콜 acme-tls/1을 협상하면, 당신 서버가 챌린지 응답을 확장에 담은 자체 서명 인증서로 답한다. 80번 포트도, 평소 웹 콘텐츠도 건드리지 않아, TLS를 종단하는 로드밸런서가 443을 소유하고 검증을 거기서 처리하고 싶을 때 맞는 도구다. 대부분은 쓸 일이 없고, 그래도 괜찮다.

자동화가 판돈 전부였다

발급을 API 호출로 보는 순간, 인증서 수명 단축 이야기는 별개의 논쟁이길 그만두고 같은 이야기가 된다.

90일 수명은 Let’s Encrypt가 내놨을 때 적대적으로 느껴졌고, 갱신이 여전히 사람의 잡일이었다면 정말로 적대적이었을 것이다 — 아무도 1년에 네 번 CSR을 폼에 붙여넣고 싶지 않다. 그게 통하는 건 오직 ACME가 갱신을 무인화했기 때문이다. 점점 더 짧은 인증서를 향한 업계의 행진 — 90일 아래로, 2029년엔 47일을 향해 — 이 견딜 만한 건 통제 증명 단계가 사람이 기억하는 작업이 아니라 데몬이 말하는 프로토콜이기 때문이다. 일부 서버, Caddy가 가장 분명한 예인데, 이를 결론까지 밀어붙여 그냥 스스로 ACME를 처리하고 운영자에겐 인증서 관리를 전혀 노출하지 않는다. 인증서는 서버를 켜는 일의 구현 세부가 됐다.

그게 이 거래의 좋은 버전이다: 만료일이 토요일 장애의 원천이길 그만두고, 짧은 수명이 유출된 키가 위험한 채로 남는 창을 줄인다.

우리가 챌린지에 넘긴 것

자동화는 위험을 없애지 않았다; 옮겼을 뿐이고, 어디로 갔는지 정직하게 볼 가치가 있다.

옛 모델에서, 소유하지 않은 도메인의 인증서를 위조하려면 인증기관을 이겨야 했다 — 검증 담당 직원을 사회공학으로 속이거나, 서명 키를 탈취하거나. ACME에서는, 검증 한 번의 길이 동안 도메인 통제 확인을 이기면 된다. 공격자가 당신 이름에 대해 http-01에 답할 수 있다면 — 80번 포트를 탈취하거나, 공유 호스트의 경로를 오염시키거나, 전송 중인 CA의 검증 요청을 가로채서 — 진짜로 공개 신뢰되는 인증서를 당신 도메인에 대해 받을 수 있고, 그건 정확히 당신 것처럼 보일 것이다. 실제로 당신 것 같은 것이니까. 위협은 “금고를 털어라”에서 “가져오기(fetch)와의 30초 경주에서 이겨라”로 옮겨갔다. 명백히 더 나쁘진 않지만, 다르다. 그리고 그것이 진지한 CA들이 이제 여러 네트워크 지점에서 동시에 검증하는 이유다: CA가 당신 챌린지를 인터넷 곳곳 여러 곳에서 확인하고 그들이 모두 일치해야 한다면, 국지적 경로 탈취는 더는 속이지 못한다. 그 완화책은 바로 자동화가 검증 순간을 공격할 가치가 있는 것으로 만들었기에 존재한다.

그리고 집중의 대가가 있다. 하나의 프로토콜, 소수의 ACME 인증기관, 그리고 수억 도메인을 덮는 단일 발급자는 이제 실패 양식이 상관된다는 뜻이다. Let’s Encrypt가 2020년 CAA 레코드를 재확인하는 방식에서 버그를 찾았을 때, 해법은 짧은 통보로 수백만 인증서를 폐기하는 것이었다 — 웹의 큰 조각이 한꺼번에 겪는 소방 훈련. 웹의 큰 조각이 같은 방식으로 같은 곳에서 갱신하기 때문이다. 자동화는 지렛대 하나를 준다, 누군가 그걸 당겨야 하기 전까지는 멋진 지렛대를.

정직한 버전

ACME는 잡일을 자동화한 게 아니다. 그 잡일이 무엇을 증명하고 있었는지를 다시 정의했다. 옛 의식은 도메인 통제를 서류 행위로 분장시켰다 — 조직의 사람이 요청을 넣고 이메일을 읽었다. ACME는 그 분장을 벗기고 증명을 주장과 정확히 일치시켰다: 지금 이 이름에 대해 챌린지에 답할 수 있는 기계가, 지금 이 이름의 인증서를 받는다. 그건 더 작고 더 정직한 주장이며, 60일마다 다시 주장할 만큼 싸다.

이 구조물 전체는 하중을 지는 가정 하나에 기댄다 — 챌린지에 답하는 것이 당신이 아닌 누구에게나 어렵다는 가정. 그건 도메인 검증의 늘 물렁한 지점이었고, ACME가 만든 게 아니다. ACME가 한 것은 그 확인을 하루에 백만 번, 자동으로, 암호화된 웹 대부분에 대해 돌린 것이다. 가정이 지켜지면 당신은 인증서를 다시는 생각하지 않는데, 그게 핵심이다. 지켜지지 않으면, 발급을 수월하게 만든 그것이 곧 실패를 넓게 만드는 그것이다. 이 둘은 동시에 참이고, 아닌 척하는 것이 놀라는 방법이다.

토론 참여

← 블로그로 돌아가기