2014년 10월 14일, 구글의 세 연구자 — Bodo Möller, Thai Duong, Krzysztof Kotowicz — 가 암호화된 웹 세션의 평문을 한 바이트씩 벗겨내는 공격을 공개했다. 이름은 POODLE, Padding Oracle On Downgraded Legacy Encryption의 억지스러운 약자다. 그런데 이 억지 약자가 일을 제대로 한다. 공격의 핵심은 전부 저 이름 안에 들어 있다. 천천히 읽으면 된다.
POODLE이 깬 프로토콜은 SSL 3.0이었다. SSL 3.0은 1996년에 나왔다. 2014년쯤엔 이미 TLS 1.0, 1.1, 1.2로 세 번 대체된 뒤였다. 세 개의 더 새롭고 더 나은 프로토콜이 널리 깔려 있었고, 사실상 아무 브라우저도 SSL 3.0을 골라서 쓰지 않았다. 그러니 흥미로운 질문은 “18년 된 프로토콜을 어떻게 깼나”가 아니다. 선택 평문·암호문 공격에 대한 현대적 이해가 생기기도 전에 설계된 18년짜리 프로토콜은 어디든 물렁한 데가 있기 마련이다. 진짜 질문은 이거다. 아무도 SSL 3.0을 안 쓰는데, 그걸 깬 게 대체 왜 문제가 되나?
답이 이 글의 전부고, 암호학과는 아무 상관이 없다.
SSL 3.0의 물렁한 지점
실제 약점부터 보자. 작고 구체적이니까. SSL 3.0은 블록 암호를 CBC 모드로 쓸 때 각 레코드를 블록 크기의 배수로 맞추려고 끝에 몇 바이트의 패딩을 붙인다. 결함은 여기 있다. SSL 3.0은 패딩의 마지막 바이트를 패딩 길이로 정하면서, 나머지 패딩 바이트의 내용은 규정하지 않았다. 그리고 결정적으로, 그 바이트들은 레코드의 MAC이 보호하지 않는다. 무결성 검사는 메시지와 패딩 길이는 서명하지만 패딩 채움 바이트 자체는 서명하지 않는다.
그게 균열이다. 수신 측은 CBC 레코드를 복호화할 때 MAC을 확인하고, 마지막 바이트를 봐서 패딩을 얼마나 잘라낼지 판단한다. 하지만 패딩 바이트에 특정 값이 들었는지는 검증하지 않는다. 스펙이 그래야 한다고 안 했으니까. 그래서 수신 측은 길이 바이트만 그럴듯한 값으로 나오면, 나머지 패딩이 쓰레기여도 그 레코드를 받아들인다.
연결 중간에 앉은 공격자에게 (a) 암호문을 조작하고 (b) 서버가 그 결과를 받아들이는지 거부하는지 지켜볼 능력을 주면, 패딩 오라클이 완성된다. 공격자는 원하는 바이트가 든 암호문 블록을 복사해 패딩이 있는 끝자리에 붙여 보낸다. 대개는 서버가 거부한다. 하지만 CBC 복호화가 직전 암호문 블록을 XOR하기 때문에, 대략 256번에 1번꼴로 조작된 마지막 바이트가 유효한 패딩처럼 보이는 값에 정확히 떨어지고 — 서버가 받아들인다. 그 수락이 평문 한 바이트를 흘린다. 평균 비용은 바이트당 약 256번의 요청. 지루하다. 하지만 컴퓨터는 지루함을 개의치 않는다.
그런데 아무도 SSL 3.0을 안 쓰는데, 어떻게 거기로 끌고 가나
여기서 공격은 암호학 얘기를 멈추고, 소프트웨어에 새겨진 인간의 반사작용 얘기로 넘어간다.
오라클을 돌리려면 공격자는 두 가지를 동시에 가져야 하고, 이게 BEAST/CRIME/POODLE 계열 전체의 서명이다. 하나는 네트워크 공격자여야 한다는 것 — 회선 위에서 트래픽을 보고 고칠 수 있는, 카페 와이파이나 장악된 공유기면 되는 위치. 다른 하나는 웹 공격자여야 한다는 것 — 피해자의 브라우저에서 자바스크립트를 돌릴 수 있는, 광고 네트워크 하나나 피해자가 함께 열어둔 HTTP 페이지 하나면 되는 능력. 자바스크립트가 표적 사이트로 수백 개의 요청을 공격자가 정한 정렬로 쏘아, 세션 쿠키 같은 비밀 바이트를 오라클이 읽을 수 있는 자리로 밀어 넣는다. 네트워크 계층이 조작하고 수락/거부를 지켜본다. 2014년 기준 어느 쪽도 이국적인 능력이 아니었다.
문제는 두 능력 모두 피해자가 SSL 3.0으로 말하고 있다고 전제한다는 것이다. 2014년의 피해자 브라우저는 TLS 1.2를 원했다. 그래서 공격자는 다운그레이드를 강제해야 한다. 그리고 할 수 있다 — 뭔가를 깨서가 아니라, 웹 전체가 합의해 둔 예의를 악용해서.
다운그레이드 댄스
우리를 죽인 반사작용이 이거다. TLS 클라이언트는 자기가 지원하는 가장 높은 버전으로 협상하려 한다. 그런데 진짜 인터넷은 자기가 모르는 버전을 광고하는 ClientHello에 사레들리는 고장난 서버와 멍청한 미들박스로 가득하다 — 우아하게 한 단계 낮추는 게 아니라 그냥 연결을 끊어버린다. 그런 사이트를 계속 굴리려고 브라우저는 우회책을 채택했다. 핸드셰이크가 실패하면 더 낮은 버전으로 재시도한다. 또 실패하면 또 낮춘다. TLS 1.2 → 1.1 → 1.0 → SSL 3.0, 바닥까지.
지저분한 세상을 위한 합리적인 접착제다. 동시에 중간자에게 주는 선물이기도 하다. 공격자는 TLS 1.2 핸드셰이크를 깰 필요가 없다 — 그냥 죽이면 된다. TCP 리셋을 보낸다. 브라우저는 친절하게도 고장난 서버 중 하나를 만났다고 짐작하고 한 단계 낮춰 재시도한다. 또 리셋. 또. 몇 번의 왕복이면 브라우저는 둘 다 현대 TLS를 완전히 지원하는 두 기계 사이에서 제 발로 SSL 3.0까지 걸어 내려가 있고, 이제 패딩 오라클이 판에 오른다. 브라우저는 나쁜 암호를 쓰도록 속은 게 아니다. 고장난 서버인 척하는 공격자에게 예의를 차리도록 속은 것이다.
약자가 “Downgraded”로 시작하는 이유가 이거다. 레거시 암호는 애초에 핵심이 아니었다. 거기로 손을 뻗으려는 그 의향이 핵심이었다.
아무도 인정하기 싫어했던 진짜 해법
연구자들은 임시 패치를 내놨다. TLS_FALLBACK_SCSV, 클라이언트가 폴백 재시도를 할 때 끼워 넣는 특수 신호다. 지금 협상 중인 버전보다 높은 버전을 지원하는 서버가 그 신호를 보면, 일어나선 안 될 다운그레이드가 벌어지고 있음을 알아채고 연결을 중단한다. 조용하고 악용 가능한 다운그레이드를, 시끄럽고 거부되는 다운그레이드로 바꾼다. 빠르게 배포된 좋은 엔지니어링이다.
하지만 TLS_FALLBACK_SCSV는 그 댄스에 붙인 패치지 치료제가 아니다. 강제된 다운그레이드는 막지만 목적지 자체를 없애진 못한다. SSL 3.0이 여전히 컴파일되어 있고 여전히 제안되는 한, 그건 여전히 도달할 수 있는 장소였다. 유일한 진짜 해법은 다들 수년간 피해 온 볼품없는 그것이었다. SSL 3.0을 끈다. 클라이언트에서 지운다. 서버에서 거부한다. 아예 말할 수 없게 만든다.
정확히 그 일이 벌어졌고, 그 결단을 마침내 공급한 게 POODLE이었다. 공개 며칠 만에 브라우저들은 SSL 3.0을 없애겠다고 발표했고, 서버들은 그것을 뜯어냈고, CDN들은 통째로 껐다. 2015년 6월, IETF가 RFC 7568로 공식화했다. 그 문서의 존재 이유는 정장을 차려입은 한 문장이다. SSLv3는 사용해서는 안 된다(MUST NOT). 수년간 “폐기됨”이고 “권장하지 않음”이고 “레거시”였던 프로토콜이, 드디어 실제로 죽었다.
반전: POODLE은 TLS도 물었다
교훈을 더 날카롭게 만드는 후일담이 있다. 두 달 뒤인 2014년 12월, 연구자들은 SSL 3.0뿐 아니라 TLS에서도 통하는 POODLE 변종을 찾아냈다 — CVE-2014-8730. TLS 1.0부터 1.2는 원래 결함을 고쳤다. 스펙이 패딩 바이트는 정해진 값을 가지며 검사되어야 한다고 요구한다. 그런데 일부 구현 — 유명하게는 큰 사이트 앞단에서 TLS를 종단하던 특정 F5·A10 로드밸런서 — 은 실제로 그 검사를 하지 않았다. 문서상으로는 프로토콜을 고쳐 놓고, 코드에는 SSL 3.0의 허술한 패딩 처리를 그대로 베껴 둔 것이다. 인터넷 HTTPS 사이트의 대략 열에 하나가, TLS에서는 불가능해야 할 공격에 TLS로 노출됐다.
그 변종은 TLS의 결함이 아니다. 이미 따른다고 주장하는 스펙을 실제로 따르지 않은 결함이다. 그리고 “우리는 폐기했다”와 “우리는 취약하지 않다”가 다른 문장인 이유를 못 박는다. 위험은 애초에 옛 프로토콜의 이름에 갇혀 있던 적이 없다.
프로토콜은 실제로 어떻게 죽는가
POODLE을 다른 느린 암호 참사들 옆에 세우면 같은 모양이 보인다. SSL 3.0은 수년간 약하다고 알려져 있었다. 공식적으로 폐기됐고, 권장되지 않았고, 레거시로 딱지 붙었다. 그 무엇도 그것을 죽이지 못했다. 폐기는 권고이고, 코드는 여전히 거기 있었고, 여전히 도달 가능했고, 여전히 예의 바른 폴백 한 번 거리에 있었으니까. 표준 기구가 눈살을 찌푸린다고 프로토콜이 죽는 게 아니다. 아무도 일부러 안 고른다고 죽는 것도 아니다. 클라이언트와 서버가 그것을 말하기를 딱 잘라 거부할 때 — 목적지가 인기 없어진 게 아니라 아예 사라졌을 때 — 비로소 죽는다.
우리는 이걸 계속 배우면서 계속 믿지 않는다. 수출용 암호 스위트는 그것을 요구한 법이 폐지되고도 15년을 도달 가능한 채로 남아 있었다. SSL 3.0은 다들 낡았다고 합의하고도 수년을 도달 가능한 채로 남아 있었다. “우리는 이제 이걸 안 쓴다”와 “우리는 이걸 쓸 수 없다” 사이의 틈, 공격은 거기 산다. 그리고 그 틈을 닫는 데는 늘 같은 것이 필요하다. 영리한 패치가 아니라, 뭔가를 지우고 거기 조용히 기대고 있던 낡은 것이 부서지든 말든 감수할 배짱. POODLE의 진짜 기여는 패딩 오라클이 아니었다. CBC 패딩 오라클 공격은 2002년 Vaudenay까지 거슬러 올라가고 2013년 Lucky Thirteen으로 이미 TLS를 겨눈 적이 있다. 기법은 옛날 얘기였다. POODLE의 기여는, 업계가 10년 전에 뽑았어야 할 플러그를 마침내 뽑게 만들 만큼 무섭고 또 이름이 좋았다는 것이다.
암호는 핑계였다. 반사작용이 취약점이었다. 그리고 해법은 삭제 키였다.