인증 핸드셰이크
연결이 수립되면(TLS를 쓴다면 TLS 완료 후) 서버가 먼저 challenge를 보냅니다. 개행(\n)으로 구분되는 텍스트 라인 2개짜리 교환으로, 연결당 1회 수행됩니다.
클러스터의 Raft 포트도 같은 핸드셰이크를 씁니다 — 검증에 쓰는 토큰 집합만 다릅니다 (클라이언트 포트 =
client_tokens, Raft 포트 =cluster_tokens).
1. Challenge (서버 → 접속자)
구조
예제 refresh
<hash> <nonce>\n 예: SHA-256 aB3dEf_g\n| 필드 | 내용 |
|---|---|
hash | 다이제스트 알고리즘 이름. 현재 항상 SHA-256이며 서버도 이것만 허용 |
nonce | base64url 알파벳 8자 난수. 개행 프레이밍이므로 길이는 추후 늘 수 있음 — 접속자는 첫 공백 뒤 전체를 nonce로 취급해야 합니다 |
2. Response (접속자 → 서버)
구조
예제 refresh
<digest>\n
digest = base64url_nopad( SHA-256( token_bytes ∥ nonce_bytes ) )(∥는 바이트 연결 — 토큰의 원시 바이트 뒤에 nonce 문자열의 ASCII 바이트를 그대로 이어 붙입니다)
- base64url은 패딩 없는 url-safe 알파벳(
A–Z a–z 0–9 - _)입니다. SHA-256 다이제스트는 항상 43자가 됩니다. - 서버는 설정된 토큰 목록 전체와 상수시간 비교로 검증합니다 — 어느 토큰이 맞았는지, 어디까지 일치했는지가 타이밍으로 새지 않습니다.
- 토큰을 설정하지 않은 서버는 빈 토큰
""하나를 허용합니다. 그 경우digest = base64url(SHA-256(nonce))를 보내면 통과합니다. 핸드셰이크 절차 자체는 생략할 수 없습니다. - 서버가 읽는 응답 라인 상한은 256바이트입니다.
3. 결과
- 성공: 서버는 아무것도 보내지 않고 곧바로 락 프로토콜로 전환합니다 — 첫 요청을 바로 보내면 됩니다.
- 실패:
E auth_failed를 보낸 뒤 연결을 닫습니다. 핸드셰이크 도중 연결이 끊기거나 라인이 비정상이면 오류 프레임 없이 조용히 닫힐 수도 있습니다.
용어
- challenge-response — 비밀(토큰)을 그대로 보내는 대신, 서버가 낸 일회용 문제(challenge)를 비밀로 풀어 그 답만 보내는 인증 방식.
- nonce — 한 번만 쓰는(number used once) 난수. 매 접속마다 달라지므로, 훔친 응답을 재사용하는 replay 공격이 통하지 않습니다.
- 상수시간 비교(constant-time compare) — 어디까지 일치했는지와 무관하게 항상 같은 시간이 걸리는 비교. 응답 시간 차이로 비밀이 새는 타이밍 공격을 막습니다.
- base64url — URL에 안전한 알파벳(
A–Z a–z 0–9 - _)만 쓰는 base64 변형. 여기서는 패딩(=) 없이 씁니다.