menuTicketing
문서search

인증 핸드셰이크

클라이언트 포트와 클러스터의 Raft 포트가 완전히 같은 규격을 쓰는 공통 구간입니다. 연결이 수립되면(TLS를 쓴다면 TLS 완료 후) 서버가 먼저 challenge를 보냅니다. 개행(\n)으로 구분되는 텍스트 라인 2개짜리 교환으로, 연결당 1회 수행됩니다.

포트에 따라 달라지는 것은 검증에 쓰는 토큰 집합뿐입니다 — 클라이언트 포트는 client_tokens, Raft 포트는 cluster_tokens로 검증합니다. Raft 포트 쪽 토큰 규칙(보내는 토큰, 무중단 교체)은 접속 수립 · 인증을 참고하세요.

흐름

접속자 (클라이언트 · 피어)
서버
TCP 접속 · (TLS 핸드셰이크)
challenge: SHA-256 ␣ nonce
digest = SHA-256(token ∥ nonce) · 43자
토큰 목록 전체와 상수시간 비교
성공 → 무응답, 곧바로 본 프로토콜 시작
첫 요청 바로 전송 (예: A · 획득)
실패했다면
E · auth_failed → 연결 닫힘
요청응답

1. Challenge (서버 → 접속자)

구조refresh
<hash> <nonce>\n            예: SHA-256 aB3dEf_g\n
필드내용
hash다이제스트 알고리즘 이름. 현재 항상 SHA-256이며 서버도 이것만 허용
noncebase64url 알파벳 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. 결과

  • 성공: 서버는 아무것도 보내지 않고 곧바로 본 프로토콜로 전환합니다 — 클라이언트 포트는 바이너리 프레임 락 프로토콜, Raft 포트는 길이-프리픽스 RPC입니다. 첫 요청을 바로 보내면 됩니다.
  • 실패: E auth_failed를 보낸 뒤 연결을 닫습니다. 핸드셰이크 도중 연결이 끊기거나 라인이 비정상이면 오류 프레임 없이 조용히 닫힐 수도 있습니다.

용어

  • challenge-response — 비밀(토큰)을 그대로 보내는 대신, 서버가 낸 일회용 문제(challenge)를 비밀로 풀어 그 답만 보내는 인증 방식.
  • nonce — 한 번만 쓰는(number used once) 난수. 매 접속마다 달라지므로, 훔친 응답을 재사용하는 replay 공격이 통하지 않습니다.
  • 상수시간 비교(constant-time compare) — 어디까지 일치했는지와 무관하게 항상 같은 시간이 걸리는 비교. 응답 시간 차이로 비밀이 새는 타이밍 공격을 막습니다.
  • base64url — URL에 안전한 알파벳(A–Z a–z 0–9 - _)만 쓰는 base64 변형. 여기서는 패딩(=) 없이 씁니다.