사전 챌린지-응답
프로토콜

챌린지-응답

gabury1고친 사람 github-actions[bot]

챌린지-응답은 상대가 정말 비밀을 가졌는지 문제를 내고 답을 받아 확인하는 방식입니다. 증명하는 쪽은 비밀을 보내지 않고 그 비밀로 푼 답만 돌려줍니다. 문제가 매번 바뀌어서 엿들은 답을 다음번에 다시 쓸 수 없습니다. 웹에서는 서버의 인증 요구에 클라이언트가 신원 정보를 실어 다시 보내는 한 왕복도 이 이름으로 부릅니다.

쉽고 빠른 이해

챌린지-응답은 비밀을 보내지 않고도 비밀을 가졌다는 것을 보이는 방법입니다. 숫자를 넣으면 답을 계산해 주는 은행 보안 기기가 그 예입니다. 은행이 매번 다른 숫자를 문제로 내면 사용자는 그 숫자를 기기에 넣습니다. 기기가 계산해 보여 준 답만 은행에 입력합니다.

이게 없으면 비밀을 통째로 보내야 합니다. 중간에서 한 번 엿들은 사람이 그 비밀로 언제든 로그인합니다.

어떻게 도는가:

  1. 서버가 매번 새로 만든 무작위 값을 문제로 보냅니다
  2. 클라이언트가 자기 비밀과 그 값을 섞어 계산한 답을 돌려줍니다
  3. 서버가 같은 계산을 해 보거나 답을 검사해 맞는지 봅니다

대가도 있습니다. 문제를 받아야 답을 만들 수 있어서 왕복이 하나 늘어납니다. 서버는 자기가 낸 문제를 기억해야 합니다. 비밀을 양쪽이 나눠 갖는 방법이면 서버도 그 비밀을 쥐고 있어야 합니다.

그래서 비밀이 기기 밖으로 나가면 안 되거나 암호화되지 않은 길을 지날 때 이 방식을 씁니다. 연결을 이미 암호화했다면 흔히 비밀번호를 있는 그대로 싣습니다.

상세

챌린지-응답은 인증에 쓰는 주고받기 방식입니다. 인증은 상대가 자기가 말하는 그 사람이 맞는지 확인하는 일입니다. 로그인이 가장 흔한 인증입니다.

이름의 두 낱말은 주고받는 두 메시지를 가리킵니다. 챌린지는 확인하는 쪽이 내미는 문제입니다. 응답은 증명하는 쪽이 그 문제에 맞춰 돌려주는 답입니다.

숫자를 넣으면 답을 계산해 보여 주는 은행 보안 기기가 그 예입니다. 은행은 로그인할 때마다 화면에 새 숫자를 띄웁니다. 사용자는 그 숫자를 기기에 넣습니다. 기기가 계산해 보여 준 답을 은행에 입력합니다.

은행 화면의 숫자가 챌린지입니다. 입력한 답이 응답입니다. 기기 안에는 은행과 나눠 가진 비밀이 들어 있습니다. 같은 숫자를 넣어도 기기마다 다른 답이 나오는 까닭입니다.

아래에서는 비밀을 보내지 않고 증명하는 원리부터 짚습니다. 이어서 한 번 주고받는 순서와 매번 새 문제를 내야 하는 까닭이 나옵니다. 끝으로 HTTP(HyperText Transfer Protocol, 하이퍼텍스트 전송 프로토콜)가 같은 이름을 조금 넓은 뜻으로 쓰는 경우와 이 방식의 대가를 다룹니다.

비밀을 보내지 않는 증명

비밀을 통째로 보내는 로그인과 견주면 챌린지-응답이 왜 생겼는지 드러납니다.

가장 단순한 인증은 비밀번호를 있는 그대로 보내는 것입니다. 서버는 받은 값이 저장한 값과 같은지만 봅니다. 오가는 길에서 누가 그 값을 엿들으면 끝입니다. 엿들은 사람은 같은 값을 보내 언제든 로그인합니다.

챌린지-응답은 비밀 대신 비밀로 계산한 값을 보냅니다. 서버가 문제를 내면 클라이언트는 자기 비밀과 그 문제를 섞어 답을 계산합니다. 오가는 것은 문제와 답뿐입니다. 비밀은 클라이언트 밖으로 나가지 않습니다.

답을 계산할 때는 거꾸로 풀 수 없는 계산을 씁니다. 문제와 답을 둘 다 엿들어도 거기서 비밀을 되짚어 낼 수 없어야 하기 때문입니다. 그 계산은 아래 「답을 만드는 두 방법」에서 다룹니다.

한 번 주고받는 순서

로그인 한 번에 메시지가 네 번 오갑니다. 등장하는 쪽은 증명하는 클라이언트와 확인하는 서버 둘입니다.

챌린지에 담는 값은 대개 논스입니다. 논스는 한 번만 쓰고 버리는 무작위 값입니다. 문제를 매번 바꾸는 일을 이 값이 맡습니다.

sequenceDiagram
    participant 클라이언트
    participant 서버
    클라이언트->>서버: 로그인하겠다
    서버->>클라이언트: 챌린지 · 새로 만든 논스
    Note over 클라이언트: 비밀과 논스로 답을 계산
    클라이언트->>서버: 응답 · 계산한 답
    Note over 서버: 답이 맞는지 확인
    서버->>클라이언트: 성공 또는 실패

비밀번호를 있는 그대로 보내는 방식보다 왕복이 하나 많습니다. 문제를 받기 전에는 답을 만들 수 없기 때문입니다.

논스가 매번 새로워야 하는 까닭

문제가 매번 바뀌지 않으면 가로챈 답을 다시 보내는 공격이 통합니다.

답은 비밀과 문제로 정해집니다. 문제가 늘 같으면 답도 늘 같습니다. 그러면 답은 비밀번호와 다를 것이 없습니다. 한 번 엿들은 답을 다시 보내면 로그인이 됩니다.

가로챈 메시지를 다시 보내 남인 척하는 공격을 재전송 공격이라고 합니다. 논스를 매번 새로 만들면 이 공격이 막힙니다. 지난번 답은 지난번 논스에 맞춘 답이라 이번 논스와 맞지 않습니다.

그래서 서버는 논스를 두 조건에 맞춰 만듭니다. 미리 짐작할 수 없어야 합니다. 이미 쓴 값을 다시 내지 않아야 합니다.

짐작할 수 있으면 이런 일이 생깁니다. 공격자가 다음 논스를 미리 알아내 가짜 화면에서 사용자에게 그 값을 문제로 내밉니다. 받은 답을 쥐고 있다가 진짜 서버가 그 논스를 낼 때 냅니다.

짐작할 수 없는 값은 암호용 난수 생성기로 만듭니다. 난수 생성기는 무작위 값을 뽑아 주는 도구입니다. 암호용은 앞서 나온 값들로 다음 값을 알아맞힐 수 없게 만든 것입니다.

서버는 자기가 낸 논스를 기억해 둡니다. 돌아온 답이 방금 낸 논스에 대한 것인지 봐야 하기 때문입니다. 그 논스가 이미 한 번 쓰였는지도 봅니다. 오래된 논스는 유효 시간을 두어 버립니다.

답을 만드는 두 방법

응답을 계산하는 방법은 둘입니다. 둘을 가르는 기준은 서버가 무엇을 쥐고 있느냐입니다.

앞에서 말한 「거꾸로 풀 수 없는 계산」의 대표가 해시 함수입니다. 해시 함수는 입력을 정해진 길이의 값으로 바꾸는 계산입니다. 결과에서 입력을 거꾸로 알아낼 수 없습니다.

첫째는 비밀을 양쪽이 나눠 갖는 방법입니다. 클라이언트는 비밀과 논스를 해시 함수에 넣어 답을 만듭니다. 서버도 같은 비밀로 같은 계산을 해서 두 값을 견줍니다.

서버는 비밀번호 자체 대신 그것으로 미리 계산해 둔 값을 저장하기도 합니다. 그 값만 있어도 같은 답을 계산할 수 있습니다. 그래서 이 값은 비밀과 맞먹습니다.

둘째는 공개 키 암호를 쓰는 방법입니다. 공개 키 암호는 짝을 이루는 키 둘을 쓰는 암호 방식입니다. 한쪽은 주인만 갖는 개인 키입니다. 다른 쪽은 남에게 보여 줘도 되는 공개 키입니다.

클라이언트는 개인 키로 논스에 서명합니다. 서버는 미리 등록받은 공개 키로 그 서명이 맞는지 확인합니다. 개인 키로 만들고 공개 키로 확인하는 이 값을 전자 서명이라고 합니다.

두 방법을 나란히 놓으면 이렇습니다.

비밀 나눠 갖기 공개 키 서명
응답을 만드는 법 비밀과 논스를 해시 개인 키로 논스에 서명
서버가 쥐는 것 비밀 또는 비밀과 맞먹는 값 공개 키
서버 저장소가 털리면 털린 값으로 응답을 만들 수 있다 공개 키로는 응답을 못 만든다

셋째 줄이 두 방법을 가르는 대가입니다. 나눠 갖는 방법은 서버도 같은 계산을 해야 합니다. 그래서 서버도 비밀을 쥐고 있어야 합니다. 서버에서 그 값이 새면 공격자가 곧장 응답을 만듭니다.

나눠 갖는 방법의 보기로는 HTTP 의 Digest 인증이 있습니다. 공개 키 방법의 보기로는 비밀번호 없이 기기 안의 키로 로그인하는 패스키가 있습니다.

HTTP 가 쓰는 넓은 뜻

웹 서버가 인증을 요구하는 한 왕복도 챌린지와 응답이라는 이름을 씁니다. 서버와 클라이언트가 주고받는 헤더로 그 왕복을 따라갑니다.

HTTP 서버는 인증이 필요한 자원에 증명 없는 요청이 오면 401 상태 코드로 거절합니다. 이 응답에 WWW-Authenticate 헤더를 실어 어떤 방식으로 증명하라고 알립니다. 이름 앞의 WWW 는 World Wide Web(월드 와이드 웹)의 줄임말입니다. HTTP 는 이 요구를 챌린지라고 부릅니다.

클라이언트는 요구받은 방식대로 자격 증명을 Authorization 헤더에 실어 요청을 다시 보냅니다. 자격 증명은 신원을 증명하는 값입니다. 비밀번호나 로그인 뒤 받은 토큰 문자열이 그렇습니다.

이 다시 보낸 요청이 응답입니다. 이름은 응답이지만 서버가 돌려주는 HTTP 응답이 아닙니다. 클라이언트가 보내는 요청을 가리킵니다.

HTTP 는 이 틀 위에 여러 인증 스킴을 얹습니다. 인증 스킴은 자격 증명을 무엇으로 어떻게 싣는지 정한 방식입니다. 어느 스킴을 쓰느냐에 따라 이 왕복이 앞에서 본 좁은 뜻의 챌린지-응답이 되기도 하고 아니기도 합니다.

Basic 스킴은 챌린지에 논스가 없습니다. 클라이언트는 아이디와 비밀번호를 누구나 되돌릴 수 있는 꼴로 옮겨 싣습니다. 주고받는 모양만 챌린지-응답입니다. 비밀은 오가는 길에 그대로 드러납니다.

Digest 스킴은 챌린지에 논스를 담습니다. 아래는 서버가 보내는 챌린지입니다. realm 은 서버가 붙인 보호 구역의 이름입니다.

http
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Digest realm="admin", nonce="7ypf3xlj"

클라이언트는 비밀번호와 이 논스로 해시 값을 계산해 다시 보냅니다. 아래는 주요 매개변수만 추린 요청입니다. 보기 좋게 두 줄로 나눴지만 보내는 것은 헤더 한 줄입니다.

http
GET /admin HTTP/1.1
Authorization: Digest username="kim", realm="admin",
    nonce="7ypf3xlj", response="6629fae4"

response 가 계산한 답입니다. 비밀번호는 헤더 어디에도 없습니다. Digest 는 좁은 뜻으로도 챌린지-응답입니다.

막는 공격과 못 막는 공격

공격 넷을 놓고 막는지 못 막는지 가르면 이렇습니다.

공격 막나 까닭
오가는 비밀번호 엿듣기 ✓ 비밀이 오가지 않는다
엿들은 답 다시 보내기 ✓ 논스가 매번 바뀐다
엿들은 답으로 약한 비밀번호 맞히기 ✗ 후보를 넣어 같은 답이 나오나 볼 수 있다
문제와 답을 중간에서 실시간으로 나르기 ✗ 공격자는 문제를 풀 필요가 없다

아래 두 줄이 이 방식만으로는 못 막는 공격입니다.

셋째 줄은 사전 공격입니다. 엿들은 사람은 문제와 답을 둘 다 가졌습니다. 흔한 비밀번호를 하나씩 넣어 계산해 보다가 같은 답이 나오면 비밀번호를 찾은 것입니다. 비밀을 나눠 갖는 방법에서 그 비밀이 사람이 고른 비밀번호일 때 생기는 약점입니다.

넷째 줄은 중간자 공격입니다. 공격자는 가짜 서버로 사용자를 맞습니다. 진짜 서버가 낸 문제를 사용자에게 건넵니다. 사용자가 푼 답은 진짜 서버에 건넵니다.

문제가 매번 새로워도 이 공격은 통합니다. 지금 이 순간의 답을 나르기 때문입니다. 그래서 막는 방법이 둘입니다.

하나는 오가는 길을 HTTPS(HyperText Transfer Protocol Secure, 보안 하이퍼텍스트 전송 프로토콜)로 암호화하는 것입니다. HTTPS 는 HTTP 를 암호화한 연결에 실어 보내는 방식입니다.

다른 하나는 답을 계산할 때 접속한 사이트 주소까지 섞어 넣는 것입니다. 가짜 사이트에서 계산한 답에는 가짜 주소가 섞여 있습니다. 그래서 진짜 서버에서는 맞지 않습니다. 패스키가 이 방법을 씁니다.

기본 꼴은 한 방향입니다. 클라이언트만 증명합니다. 서버가 진짜인지는 확인하지 않습니다. 양쪽이 서로 문제를 내면 둘 다 증명됩니다. 이것을 상호 인증이라고 합니다.

쓸 때와 안 쓸 때

비밀이 기기 밖으로 나가면 안 될 때 이 방식을 고릅니다. 암호화되지 않은 길을 지나야 할 때도 그렇습니다. 패스키처럼 기기 안의 개인 키로 로그인하는 방식이 앞의 경우입니다.

연결을 HTTPS 로 이미 암호화했다면 비밀번호나 토큰을 있는 그대로 싣는 방식이 흔합니다. 오가는 길의 엿듣기는 연결의 암호화가 막습니다. 그러면 아래 대가를 치를 까닭이 줄어듭니다.

대가 까닭
왕복이 하나 는다 문제를 받아야 답을 만든다
서버가 상태를 쥔다 낸 논스와 쓴 논스를 기억해야 한다
서버가 비밀을 쥔다 비밀을 나눠 갖는 방법이면 서버도 같은 계산을 해야 한다

관련 항목

챌린지-응답이 쓰이는 인증 틀

인증 · HTTP 인증 · 인증 스킴 · WWW-Authenticate · Authorization 헤더 · HTTP 401 · 자격증명

챌린지-응답을 채택한 인증 방식

Digest 인증 · CHAP · SCRAM · NTLM · 패스키 · WebAuthn · 클라이언트 인증서

응답을 만들고 확인하는 암호 도구

논스 · 난수 생성기 · 해시 함수 · 메시지 인증 코드 · HMAC · 전자 서명 · 공개 키 암호 · 개인 키 · 공개 키

챌린지-응답이 막는 공격

재전송 공격 · 도청 · 자격 증명 탈취

챌린지-응답만으로 못 막는 공격

사전 공격 · 무차별 대입 공격 · 중간자 공격 · 피싱 · 릴레이 공격

챌린지-응답을 보강하는 수단

HTTPS · TLS · 상호 인증 · 다중 인증

챌린지-응답과 겨루는 인증 방식

Basic 인증 · Bearer 토큰 · 비밀번호 · 일회용 비밀번호 · 영지식 증명

이름이 겹쳐 헷갈리는 이웃

CAPTCHA · 챌린지 · HTTP 응답

챌린지-응답이 속하는 상위 분류

인증 프로토콜 · 암호 프로토콜 · 프로토콜 · 보안

다른 이름: challenge-response · challenge-response authentication · 챌린지-응답 인증 · 챌린지 응답 · 도전-응답