사전 메시지 인증 코드
개념

메시지 인증 코드

gabury1고친 사람 github-actions[bot]

메시지 인증 코드는 받은 데이터가 오는 길에 바뀌지 않았는지 확인하게 해 줍니다. 약속된 상대가 보낸 것인지도 같이 가려 줍니다. 보내는 쪽은 둘만 아는 비밀 키로 짧은 값을 계산해 데이터에 붙입니다. 받는 쪽은 같은 계산을 다시 해서 두 값이 같은지 봅니다.

쉽고 빠른 이해

메시지 인증 코드는 데이터에 붙여 보내는 짧은 인증값입니다. 한 서버가 다른 서버로 보내는 웹훅 요청에 이 값을 함께 실어 보내고, 받는 쪽이 같은 값이 나오는지 보고 남이 흉내 낸 요청을 걸러 내는 일이 그런 예입니다.

이 값이 없으면 받는 쪽은 내용이 오는 길에 바뀌었는지도, 애초에 누가 보낸 것인지도 알 방법이 없습니다.

어떻게 도나:

  1. 두 쪽이 같은 비밀 키를 미리 나눠 갖습니다
  2. 보내는 쪽이 데이터와 키로 인증값을 계산해 함께 보냅니다
  3. 받는 쪽이 같은 계산을 해 보고 값이 다르면 데이터를 버립니다

대가는 키를 미리 나눠 갖고 지켜야 한다는 것입니다. 그리고 키를 가진 두 쪽은 서로 같은 값을 만들 수 있어서, 제삼자 앞에서 누가 보냈는지를 가리지는 못합니다.

상세

편지를 주고받는 두 사람이 똑같은 계산표를 한 장씩 나눠 가졌다고 해 봅시다. 편지를 부칠 때마다 내용을 그 표에 넣어 나온 짧은 숫자를 편지 끝에 적습니다. 받는 쪽은 자기 표로 같은 숫자가 나오는지 봅니다. 표가 없는 사람은 편지를 고쳐 놓고 맞는 숫자를 적을 수 없습니다.

메시지 인증 코드는 데이터와 비밀 키를 함께 넣어 계산한 짧은 값입니다. 앞의 계산표가 비밀 키이고, 편지 끝에 적은 숫자가 이 값입니다.

실제로 쓰이는 곳 하나가 웹훅입니다. 어떤 일이 벌어졌을 때 상대 서버로 곧바로 요청을 보내는 방식입니다. 보내는 쪽이 요청 본문과 키로 값을 계산해 요청에 함께 실어 보냅니다.

이 값을 붙여 보내면 받는 쪽이 두 가지를 한꺼번에 확인합니다. 하나는 내용이 오는 길에 바뀌지 않았다는 것이고, 다른 하나는 같은 키를 가진 쪽이 만들었다는 것입니다. 앞을 무결성, 뒤를 인증이라고 부릅니다.

줄여서 MAC(Message Authentication Code)이라고도 부릅니다. 계산해 붙인 값 자체를 인증 태그라고 부르기도 합니다.

비밀 키가 없으면 무엇이 모자란가

해시 함수는 데이터를 넣으면 짧은 값을 내놓습니다. 데이터가 한 비트만 달라져도 이 값이 달라지므로, 값을 견주면 내용이 바뀌었는지 알 수 있습니다. 체크섬과 순환 중복 검사도 같은 식으로 씁니다.

모자란 대목은 누구나 같은 계산을 할 수 있다는 것입니다. 중간에서 데이터를 고친 쪽이 검증값도 다시 계산해 붙이면, 받는 쪽에는 앞뒤가 맞는 한 벌이 도착합니다. 받는 쪽은 아무것도 알아채지 못합니다.

sequenceDiagram
    participant A as 보내는 쪽
    participant M as 중간에서 고친 쪽
    participant B as 받는 쪽
    A->>M: 데이터 + 검증값
    M->>M: 데이터를 고친다
    M->>M: 고친 데이터로 검증값을 다시 계산
    M->>B: 고친 데이터 + 다시 계산한 검증값
    B->>B: 앞뒤가 맞아 그대로 통과한다
    Note over M: 비밀 키가 섞이면 가운데는 맞는 값을 못 만든다

메시지 인증 코드는 그 계산에 두 쪽만 아는 비밀 키를 섞습니다. 키를 섞고 나면 이 값을 검증값이 아니라 인증값이라고 부릅니다. 키를 모르는 쪽은 고친 데이터에 맞는 인증값을 만들어 낼 수 없습니다. 덕분에 전선 잡음 같은 우연한 손상뿐 아니라 일부러 한 변조까지 걸립니다.

인증값이 오가는 순서

두 쪽이 같은 비밀 키를 이미 나눠 가졌다고 보고 한 번의 전송을 따라가 봅니다.

sequenceDiagram
    participant A as 보내는 쪽
    participant B as 받는 쪽
    A->>A: 데이터와 비밀 키로 인증값 계산
    A->>B: 데이터 + 인증값
    B->>B: 받은 데이터와 비밀 키로 다시 계산
    B->>B: 받은 인증값과 견준다
    Note over B: 같으면 받아들이고 다르면 버린다

같은 계산을 두 쪽이 한 번씩 합니다. 받는 쪽은 보낸 쪽에게 되물을 필요가 없습니다. 손에 든 데이터와 자기 키만 있으면 판정이 끝나기 때문입니다.

인증값은 데이터를 감추지 않습니다. 내용은 읽을 수 있는 채로 실려 가고, 그 옆에 값이 하나 더 붙을 뿐입니다.

무엇을 보장하고 무엇을 보장하지 않나

메시지 인증 코드가 주는 것은 앞서 말한 둘, 무결성과 인증입니다. 여기서 인증은 키를 가진 쪽이 만들었다는 뜻이지 그 사람이 누구인지까지 밝히는 것은 아닙니다.

주지 않는 것은 셋입니다.

주지 않는 것 왜 못 주나 함께 쓰는 수단
기밀성 내용을 감추지 않고 값만 덧붙인다 암호화
부인 방지 두 쪽이 같은 값을 만들 수 있다 전자 서명
재전송 막기 예전 데이터와 값을 그대로 다시 보내도 검증을 통과한다 논스 · 타임스탬프

가운데 줄이 실무에서 자주 걸리는 대목입니다. 키를 나눠 가진 두 쪽은 서로 똑같은 인증값을 만들 수 있습니다. 그래서 둘 사이에서는 상대가 보냈다고 믿을 수 있지만, 제삼자에게 저쪽이 보낸 것이라고 증명하지는 못합니다.

마지막 줄이 막는 것은 재전송 공격입니다. 지난번에 오갔던 데이터와 인증값을 통째로 복사해 다시 보내면, 그 한 벌은 원래 맞는 값이라 검증을 그대로 통과합니다. 한 번만 쓰고 버리는 값인 논스나 보낸 시각을 데이터에 섞어 계산하면 이 되풀이가 걸립니다.

인증값을 만드는 두 방식

무엇을 재료로 쓰느냐에 따라 크게 둘로 갈립니다.

하나는 해시 함수를 재료로 쓰는 방식입니다. 대표가 HMAC(Hash-based Message Authentication Code, 해시 기반 메시지 인증 코드)입니다. 키와 데이터를 그냥 이어 붙여 해시하지 않고, 해시를 두 겹으로 겹쳐 계산합니다.

flowchart TD
    subgraph s1["그냥 이어 붙이기"]
        A1["비밀 키 + 데이터"] --> A2["해시 한 번"] --> A3["인증값"]
    end
    subgraph s2["두 겹으로 겹치기"]
        B1["비밀 키 + 데이터"] --> B2["안쪽 해시"] --> B3["비밀 키 + 안쪽 해시의 결과"] --> B4["바깥쪽 해시"] --> B5["인증값"]
    end

겹치는 까닭은 이어 붙이기만 하면 뒤에 내용을 덧붙이는 길이 확장 공격이 통하기 때문입니다. 내용을 못 읽는 공격자도 원래 데이터 뒤에 자기 데이터를 붙이고 맞는 값을 함께 내밀 수 있습니다.

다른 하나는 블록 암호를 재료로 쓰는 방식입니다. 대표가 CMAC(Cipher-based Message Authentication Code, 블록 암호 기반 메시지 인증 코드)입니다. 데이터를 블록 단위로 차례차례 암호화합니다. 마지막 블록의 결과가 인증값입니다.

두 방식 말고 인증 암호도 있습니다. 암호화와 인증값 계산을 한 번에 묶은 방식입니다. 둘을 따로 부르면 어느 쪽을 먼저 할지 정해야 합니다. 그 순서를 틀리는 실수를 없애려고 나왔습니다.

인증값을 견줄 때

받은 값과 계산한 값을 견주는 일은 한 줄로 끝날 것 같지만 함정이 하나 있습니다.

흔히 쓰는 문자열 비교는 다른 글자를 만나는 순간 멈춥니다. 그러면 앞자리가 몇 개나 맞았는지에 따라 판정에 걸리는 시간이 달라집니다.

공격자는 값을 조금씩 바꿔 계속 보냅니다. 그때마다 판정에 걸린 시간을 잽니다. 앞자리를 한 글자씩 맞춰 나가면 키를 모르고도 맞는 인증값을 완성할 수 있습니다. 이렇게 걸린 시간을 단서로 삼는 공격을 타이밍 공격이라고 부릅니다.

flowchart TD
    subgraph s1["흔히 쓰는 비교"]
        A1["한 글자를 견준다"] --> A2{"다른가"}
        A2 -->|그렇다| A3["거기서 판정 끝"]
        A2 -->|아니다| A1
    end
    subgraph s2["상수 시간 비교"]
        B1["끝까지 다 견준다"] --> B2["한 번에 판정"]
    end

그래서 인증값은 길이가 같은 두 값을 끝까지 다 견주고 나서 한 번에 판정하는 상수 시간 비교로 견줍니다. 암호 라이브러리가 이 용도의 비교 함수를 따로 내놓습니다.

언제 쓰고 언제 다른 것을 쓰나

무엇을 지키려 하느냐에 따라 넷 중 하나로 갈립니다.

flowchart TD
    Q1{"지키려는 것이 저장된 비밀번호인가"}
    Q1 -->|그렇다| K["키 유도 함수"]
    Q1 -->|아니다| Q2{"고의로 고치는 것까지 막아야 하나"}
    Q2 -->|아니다| C["체크섬"]
    Q2 -->|그렇다| Q3{"제삼자가 나중에 검증해야 하나"}
    Q3 -->|그렇다| S["전자 서명"]
    Q3 -->|아니다| M["메시지 인증 코드"]

두 쪽이 비밀 키를 미리 나눠 가질 수 있고, 주고받는 데이터가 바뀌지 않았는지를 서로 확인하면 되는 상황에 씁니다. TLS(Transport Layer Security, 전송 계층 보안)나 IPsec(Internet Protocol Security, 인터넷 프로토콜 보안)처럼 키를 먼저 주고받는 프로토콜, 그리고 서버끼리 주고받는 웹훅이 그런 예입니다.

키를 나눠 가질 수 없는 상대와 주고받거나, 제삼자가 나중에 검증해야 한다면 전자 서명으로 갑니다. 서명은 만드는 키와 검증하는 키가 달라서, 검증하는 쪽이 서명을 만들어 낼 수 없습니다.

고의가 없는 손상만 걱정된다면 체크섬으로 충분합니다. 키를 나눠 갖고 지키는 비용을 치를 만큼 더 얻는 것이 없습니다.

저장된 비밀번호를 지키는 데는 쓰지 않습니다. 메시지 인증 코드는 받은 데이터가 위조됐는지를 걸러 냅니다. 저장된 비밀번호는 위조가 아니라 대입이 문제라서, 계산을 일부러 느리게 만드는 키 유도 함수가 맡습니다.

관련 항목

메시지 인증 코드를 만드는 방식

HMAC · CMAC · GMAC · Poly1305 · 인증 암호

인증값 계산에 들어가는 재료

해시 함수 · 암호학적 해시 함수 · SHA-256 · 블록 암호 · 대칭 키 · 비밀 키

같은 역할을 두고 겨루는 무결성 확인 수단

체크섬 · 순환 중복 검사 · 전자 서명 · 해시

메시지 인증 코드를 겨냥하는 공격과 그 대비

길이 확장 공격 · 타이밍 공격 · 사이드 채널 공격 · 상수 시간 비교 · 재전송 공격 · 중간자 공격 · 변조

메시지 인증 코드가 지키는 보안 성질

무결성 · 인증 · 기밀성 · 부인 방지

비밀 키를 나눠 갖고 관리하는 방법

키 교환 · 키 유도 함수 · PBKDF2 · 키 회전 · 대칭키 암호

메시지 인증 코드를 쓰는 프로토콜과 포맷

TLS · JWT · IPsec · SSH · NTS · 웹훅

다른 이름: MAC · Message Authentication Code · 인증 태그