089. CBC (Cipher Block Chaining)

⚠️ 이 문서는 ECB 모드의 패턴 노출 약점을 완벽하게 해결하기 위해, 현재 블록의 평문을 이전 블록의 암호문과 엑스오어(XOR)로 비벼서 마치 쇠사슬(Chain)처럼 모든 블록을 엮어버리는 가장 클래식하고 대중적인 블록 암호 운영 모드인 CBC를 다룹니다.

핵심 인사이트 (3줄 요약)

  1. 본질: CBC(Cipher Block Chaining) 모드는 AES 믹서기에 2번 평문을 넣기 전에, 방금 1번 믹서기에서 튀어나온 1번 암호문 결과물을 끌어와서 2번 평문과 XOR(배타적 논리합) 연산으로 섞어버린 뒤 암호화를 진행하는 연쇄 반응 구조다.
  2. 가치: 아무리 똑같은 글자(예: "AAAA")가 반복되는 평문이라도, 이전 블록에서 넘어온 완전히 다른 쓰레기 값(암호문)과 섞이기 때문에 암호문이 매번 다르게 출력되어 평문의 언어적 패턴이나 윤곽선이 100% 소멸된다.
  3. 한계: 블록들이 앞뒤로 꼬리를 물고 있기 때문에, 앞 블록의 암호화가 안 끝나면 뒷 블록은 암호화를 시작조차 할 수 없는 **직렬 처리 병목(병렬화 불가)**이라는 치명적 구조적 한계 탓에, 현대의 초고속 인터넷 환경(TLS 1.3)에서는 퇴출당했다.

Ⅰ. 개요 및 필요성 (Context & Necessity)

10GB짜리 영화 파일을 16바이트 블록으로 잘라서 각각 따로 암호화(ECB 모드)했더니, 펭귄 그림의 윤곽선이 그대로 노출되는 대참사가 일어났다. 똑같은 데이터가 들어가면 똑같은 암호문이 나오는 암호학적 정직성 때문이었다.

학자들은 고민했다. "똑같은 '사과'라는 단어가 100번 나오더라도, 첫 번째 사과는 '포도'로, 두 번째 사과는 '딸기'로 바뀌게 만들려면 어떻게 해야 할까?" 답은 **'이전 결과물의 찌꺼기를 다음 재료에 섞기'**였다. 이전 블록이 어떻게 암호화되었는지 그 결과(Ciphertext)를 가져와서, 지금 암호화할 평문(Plaintext) 위에 물감처럼 엎어버리는 것이다. 블록들이 쇠사슬(Chaining)처럼 서로 맞물려 돌아가게 되면서, CBC (암호 블록 연쇄) 모드가 탄생했다.

📢 섹션 요약 비유: 김밥을 만들 때, 1번 김밥을 자른 칼에 묻은 참기름과 밥알(이전 암호문 찌꺼기)을 안 닦고 2번 김밥을 자르고, 또 3번 김밥을 자르는 식입니다. 김밥 재료(평문)가 똑같아도, 앞 김밥에서 넘어온 얼룩 때문에 매번 미세하게 다른 맛(암호문)이 탄생합니다.


Ⅱ. CBC 모드의 핵심 작동 원리와 IV의 마법

CBC 모드는 쇠사슬처럼 블록들을 엮어간다. 그렇다면 제일 처음 들어가는 1번 블록은 도대체 누구의 찌꺼기와 섞어야 할까? 앞에 아무 블록도 없지 않은가? 여기서 도입된 것이 바로 **초기화 벡터 (IV, Initialization Vector)**다.

1. 암호화 프로세스 (Encryption)

  • 1번 블록: 1번 평문을 마구잡이 난수인 **IV(초기화 벡터)**와 ⊕(XOR) 한 뒤 AES 믹서기에 돌린다. $\rightarrow$ 1번 암호문 탄생
  • 2번 블록: 2번 평문을 방금 나온 1번 암호문과 ⊕(XOR) 한 뒤 AES 믹서기에 돌린다. $\rightarrow$ 2번 암호문 탄생
  • 3번 블록: 3번 평문을 방금 나온 2번 암호문과 ⊕(XOR) 한 뒤 돌린다. $\rightarrow$ 꼬리에 꼬리를 물고 무한 반복.

2. 복호화 프로세스 (Decryption)

암호화를 거꾸로 푼다. 이 과정의 천재적인 점은, 복호화는 병렬 처리(동시 처리)가 가능하다는 것이다! 해커든 주인이든 이미 1번 암호문, 2번 암호문, 3번 암호문을 하드디스크에 다 가지고 있다.

  • 3번 평문을 알고 싶다면? 3번 암호문을 해독기에 넣고 푼 뒤, 내 하드디스크에 있는 2번 암호문을 가져와 XOR 하면 끝난다. 2번을 다 풀 때까지 기다릴 필요가 없다.
┌─────────────────────────────────────────────────────────────────────────┐
│           CBC 모드의 암호화(직렬) vs 복호화(병렬) 비대칭 구조 시각화    │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│ [ 암호화: 앞차가 가야 뒷차가 감 (직렬 병목 ☠️) ]                        │
│   IV 난수                                                               │
│     │                                                                   │
│     ▼                  (앞의 결과가 여기까지 와야 함!)                  │
│  [평문 1] ──▶ (AES) ──┬──▶ [암호문 1 출력]                              │
│                       │                                                 │
│                       └────────┐                                        │
│                                ▼                                        │
│  [평문 2] ────────────(XOR)──▶ (AES) ──▶ [암호문 2 출력]                │
│                                                                         │
│                                                                         │
│ [ 복호화: 남을 안 기다리고 한 번에 처리 (병렬 쾌속 🚀) ]                │
│  [암호문 1] ──▶ (역 AES) ──(XOR: IV 난수 가져옴) ──▶ [평문 1 복구]      │
│                                                                         │
│  [암호문 2] ──▶ (역 AES) ──(XOR: 1번 암호문 가져옴) ─▶ [평문 2 복구]    │
│                                                                         │
│  * 결론: 암호화는 드럽게 느리지만, 복호화는 코어 개수만큼 엄청 빠르다!  │
└─────────────────────────────────────────────────────────────────────────┘

[다이어그램 해설] 이 다이어그램은 CBC의 흥망성쇠를 설명한다. 암호화를 할 때는 "1번 암호문"이 튀어나와야만 그걸 2번 평문에 비빌 수 있으므로 절대로 스킵하거나 동시에 할 수 없다 (CPU가 16코어라도 1코어만 일함). 하지만 복호화할 때는 이미 암호문들이 파일로 다 존재하므로, "2번 암호문"을 복구할 때 그냥 옆에 있는 "1번 암호문" 데이터를 갖다 쓰기만 하면 끝이다.

  • 📢 섹션 요약 비유: 도미노(CBC 암호화)를 세울 때는 앞 도미노가 무조건 넘어와야 뒷 도미노가 쓰러지는 순서(직렬)를 지켜야 해서 느립니다. 하지만 쓰러진 도미노를 다시 주워 담을 때(CBC 복호화)는 10명의 친구가 동시에 10군데에서 막 주워 담아도(병렬) 문제없이 엄청 빠릅니다.

Ⅲ. 패딩(Padding)과 패딩 오라클 공격의 비극

블록 암호는 도마(블록) 크기가 무조건 128비트(16바이트)여야 한다. 그런데 내가 보낼 평문이 14바이트라면? 2바이트가 모자라서 AES 믹서기가 안 돌아간다. 그래서 남는 공간에 쓸데없는 값(02 02 등)을 채워 넣는 것을 **패딩(Padding)**이라고 한다. (PKCS#7 패딩)

하지만 CBC 모드의 치명적인 급소는 바로 이 패딩에서 터졌다. **패딩 오라클 공격(Padding Oracle Attack)**이다.

  • 공격 원리: 해커가 암호문 1바이트를 슬쩍 변조해서 서버에 던진다. 서버가 이를 복호화한 뒤 "야! 맨 끝에 패딩 찌꺼기가 이상하게 깨졌잖아!(에러 500)"라고 반응하면, 해커는 "오, 암호문을 이렇게 건드리면 에러가 뜨네?"라며 에러 메시지의 차이를 이용(오라클)해 비밀키 없이도 평문을 야금야금 추측해 내어 벗겨버린다.
  • 영향: 이 끔찍한 공격 때문에 CBC 모드는 단독으로 쓰면 언제든 털릴 수 있다는 사망 선고를 받았고, 개발자들은 에러가 나도 절대 메시지를 다르게 뱉지 않는 무자비한 코딩(Fail-Safe)을 해야만 했다.

Ⅳ. 결론 및 세대 교체 (TLS 1.2 $\rightarrow$ TLS 1.3)

CBC 모드는 지난 20년간 인터넷 뱅킹과 웹 통신(HTTPS)을 든든하게 지켜준 위대한 영웅이었다. 쇠사슬로 묶어 패턴을 지우는 이 발상은 ECB의 악몽을 끝냈다.

하지만, (1) 암호화 시 병렬 처리가 안 되어 속도계의 병목이 되는 점, (2) 패딩 오라클 공격에 툭하면 털리는 구조적 허약함 때문에 현대 클라우드와 5G 통신 시대의 짐이 되었다. 결국 최신 웹 보안 표준인 TLS 1.3 규격에서는 이 고전 영웅 CBC 모드를 표준에서 완전히 쫓아내고(Deprecation), 그 자리를 미친 듯한 병렬 처리 속도와 인증 방패를 두른 GCM(CTR 기반) 모드에게 왕좌를 넘겨주게 된다.


📌 관련 개념 맵

  • 전제 지식: ECB (패턴이 노출되는 최악의 모드), AES 블록 암호 엔진
  • 핵심 부품: IV (초기화 벡터 - 무조건 예측 불가능한 난수여야 함)
  • 치명적 취약점: 패딩 오라클 공격 (Padding Oracle Attack), Bit-Flipping Attack
  • 도태 원인: 체인 의존성으로 인한 암호화 병렬 처리(초고속화) 불가능

👶 어린이를 위한 3줄 비유 설명

  1. 100장의 편지를 차례대로 상자에 담아 잠글 때, 1번 상자를 잠근 자물쇠의 열쇠 찌꺼기를 2번 상자 안에 집어넣고 또 잠그는 기발한 방법이에요.
  2. 이렇게 꼬리에 꼬리를 물고(체인) 찌꺼기를 계속 섞어주면, 도둑이 상자 겉모습만 보고는 안에 뭐가 들었는지 절대 유추할 수 없게 된답니다.
  3. 하지만 치명적인 단점이 있어요. 1번 상자를 다 잠그기 전에는 2번 상자 작업을 아예 시작할 수가 없어서(직렬), 공장 직원 10명이 있어도 1명만 일해야 해서 너무너무 느리답니다!