"패딩이 틀렸다"는 에러 한 줄이 암호문을 통째로 풀어준다 — CBC 패딩 오라클

“패딩이 틀렸다”는 에러 한 줄이 암호문을 통째로 풀어준다 — CBC 패딩 오라클

분류: Security · 암호 · 패딩 오라클(암호 해독) [정보성]

덱. 서버가 암호를 아무리 튼튼하게 걸어놨어도, “네가 보낸 암호문은 채움값(패딩) 규칙에 안 맞아”라는 에러 하나만 흘리면 공격자는 그 실마리만으로 평문을 한 바이트씩 복원할 수 있습니다. 열쇠를 훔치는 게 아니라, 서버에게 “이거 규칙 맞아?”만 반복해 물어보는 고전 암호 공격입니다. 연구자 Bruno Rocha Moura가 정리한 이 공격의 원리를, 실행 코드 없이 개념으로만 짚어봅니다.

리드

암호학에서 가장 무서운 누출은 열쇠 자체가 새는 것이 아니라, 시스템이 “성공/실패”를 다르게 반응하는 그 미세한 차이가 새는 것입니다. 패딩 오라클(padding oracle, “채움값이 맞는지 알려주는 신탁”) 공격은 그 대표적 사례입니다. 서버는 비밀을 말한 적이 없다고 생각하지만, 잘못된 암호문에 500 에러를 내고 제대로 된 암호문엔 다른 응답을 내는 순간, 그 응답 차이 자체가 비밀을 한 방울씩 흘리는 창구가 됩니다. 이 글은 “정보 누출은 값에서만 새는 게 아니라 반응에서도 샌다”는 원리의 교과서 같은 사례입니다.

핵심 개념 30초

  • 블록 암호와 채움값(패딩). AES 같은 현대 대칭 암호는 데이터를 일정한 크기(AES는 16바이트) 덩어리로 잘라 처리합니다. 데이터가 딱 떨어지지 않으면 남는 자리를 정해진 규칙으로 채워야 하는데, 가장 흔한 규칙이 PKCS#7입니다. 이 규칙은 “N칸이 남으면 N이라는 값을 N개 채운다”는 단순한 약속입니다. 복호화한 뒤 이 채움 규칙이 안 맞으면 대부분의 암호 라이브러리는 예외(오류)를 던집니다 — 바로 이 오류가 실마리가 됩니다.
  • 블록 체이닝(CBC). CBC(암호 블록 체이닝) 방식은 각 덩어리를 앞 덩어리의 암호문과 섞어(XOR) 처리합니다. 이 구조에는 공격자에게 유용한 성질이 하나 숨어 있습니다 — 앞 덩어리의 암호문 한 바이트를 살짝 바꾸면, 복호화된 뒤 평문의 대응 바이트가 그만큼 예측 가능하게 바뀝니다. 즉 공격자는 암호문을 조금씩 비틀어 평문이 어떻게 반응하는지를 관찰할 수 있습니다.

빈틈 — 응답이 곧 신탁이 된다

핵심은 서버가 세 가지 반응을 구분해서 보여준다는 데 있습니다. (1) 암호문을 풀었더니 채움 규칙도 맞고 내용도 정상 → 정상 응답, (2) 채움 규칙은 맞지만 내용이 이상 → 다른 오류, (3) 채움 규칙 자체가 틀림 → 서버 내부 오류. 공격자에게 필요한 정보는 오직 하나, **“방금 보낸 암호문이 올바른 채움값으로 끝났는가, 아닌가”**입니다. 서버는 이 질문에 사실상 예/아니오로 계속 대답해 주는 셈이고, 이것이 바로 “신탁(oracle)“입니다.

여기서 중요한 점은 이 신탁이 꼭 오류 코드일 필요는 없다는 것입니다. 응답 헤더의 차이, 본문 메시지의 미묘한 변화, 심지어 응답 시간의 차이(그 유명한 TLS의 Lucky Thirteen 공격이 이 시간 차이를 이용했습니다)까지 — 공격자가 “채움값이 맞았는지”를 구분할 수 있게 해주는 어떤 관찰 가능한 신호든 신탁이 됩니다.

메커니즘 — 열쇠 없이 열쇠의 일을 시킨다 (개념적으로만)

실제 공격 절차는 이렇게 흘러갑니다(원리만, 재현 코드는 싣지 않습니다). 공격자는 앞 덩어리에 해당하는 값(첫 덩어리라면 초기값 IV)을 0부터 255까지 한 바이트씩 바꿔가며 서버에 던집니다. 대부분은 채움 규칙에 안 맞아 오류가 나지만, 어느 순간 마지막 바이트가 우연히 “0x01 한 개”라는 최소 채움값을 만들면 오류가 멈춥니다. 이 순간 공격자는 수학적으로 그 바이트의 중간값(암호가 만들어낸 내부값)을 역산할 수 있습니다. 이 과정을 마지막 바이트부터 앞으로 한 칸씩 되풀이하면, 열쇠를 단 한 번도 알아내지 않고도 덩어리 전체의 평문이 드러납니다.

핵심을 강조하면 — 공격자는 암호 열쇠를 훔친 적이 없습니다. 서버에게 복호화 작업을 대신 시키고, 그 결과가 규칙에 맞는지만 반복해서 물어봤을 뿐입니다. 그런데도 로그인 토큰, API 키, 사용자 데이터처럼 그 열쇠로 보호되던 모든 것이 노출됩니다. 반대 방향, 즉 공격자가 원하는 내용을 담은 위조 암호문을 만들어내는 것도 같은 원리로 가능합니다. 이렇게 위조된 값이 명령 실행이나 파일 접근처럼 위험한 자리로 흘러들면 피해는 암호 해독을 훌쩍 넘어섭니다.

한 줄 요약. 서버가 “성공/실패”를 다르게 반응하는 그 차이 하나면, 공격자는 열쇠 없이도 서버를 시켜 암호를 풀게 만든다.

왜 반복되는가 (우리 논평)

이 공격의 뿌리는 암호가 약해서가 아닙니다. AES는 멀쩡합니다. 문제는 “내 열쇠로 암호화된 것이니 당연히 내가 만든 것이고, 따라서 믿어도 된다”는 무결성(integrity) 가정입니다. 시스템은 사용자가 보낸 암호문을 무조건 복호화하고, 그 결과를 검증 없이 신뢰합니다. 우리가 다른 글에서 반복해 짚는 패턴과 정확히 같습니다 — “이 데이터는 이미 검증됐다”는 상태를 다른 문맥에서 그대로 재사용하면, 그 경계가 곧 공격 표면이 됩니다. 앞서 다룬 Shopify 계정 탈취 사례에서 “한 곳에서 확인된 이메일”을 다른 곳이 그대로 믿었던 것과 뿌리가 같습니다. 거기선 신원 상태였고 여기선 암호문의 진위였을 뿐, “출처를 검증하지 않고 믿는다”는 실수는 똑같습니다.

또 하나의 교훈은 방어의 방향입니다. 이 문제를 오류 코드를 감추거나 요청 속도를 제한해서 막으려는 시도는 근본을 비켜갑니다. 이건 암호학적 결함이고, 암호학적 해법으로만 풀립니다 — 암호문에 **인증 태그를 붙이는 방식(GCM 같은 모드)**이나, CBC를 계속 쓴다면 암호화한 뒤 메시지 인증 코드(MAC)를 함께 검증하는 패턴을 써서, “이 암호문은 서버가 만든 정품이 맞는가”를 복호화 전에 확인해야 합니다. 즉 공격자가 임의의 암호문을 던져도 신탁이 반응조차 하지 않게 만드는 것이 정답입니다.

남는 교훈

패딩 오라클은 20년 넘은 고전 공격입니다. 그런데도 사라지지 않는 이유는, 이것이 특정 버그가 아니라 “반응의 차이가 곧 정보”라는 사고방식의 문제이기 때문입니다. 오늘 CBC에서 고쳐진 이 틈은, 내일 다른 프로토콜의 다른 “미묘한 반응 차이”에서 또 나타납니다. 그래서 우리는 개별 취약점의 재현 절차가 아니라, 시스템이 성공과 실패를 어떻게 다르게 반응하는지를 항상 의심하는 방법을 남깁니다.

References / 참고자료

  1. Bruno Rocha Moura, “CBC Padding Oracle Attacks” — 이 글이 재구성한 원 분석. AES-CBC와 PKCS#7을 예로 패딩 오라클의 복호화·암호화 공격 원리와 방어를 설명함. https://www.brunorochamoura.com/posts/cbc-padding-oracle/
  2. OWASP, “Testing for Padding Oracle” — 블랙박스·그레이박스 관점에서 이 취약점을 탐지하는 표준 지침(개념 참고).

이 글은 위 원 분석을 우리 관점에서 다시 쓰고 방어적 논평을 더한 정보성 초안입니다. 특정 문장·수식·코드·페이로드를 그대로 옮기지 않았으며, 작동하는 공격 재현 코드는 싣지 않았습니다. 원리 이해와 방어 설계를 돕기 위한 개념 설명이며, 자세한 내용은 원문을 참고하세요.