일회용 코드가 휴대폰에서는 완벽히 맞는데 서버가 같은 초에 거부할 수 있습니다. 양쪽은 공유 비밀과 현재 시간 단계로 같은 함수를 계산하므로, 시간에 대한 인식이 다르면 코드에 대한 인식도 달라집니다. 그래서 시계 오차는 단순한 불편이 아니라 보안과 관련된 결함이 됩니다.

여기서는 두 개의 시계가 중요하며, 보이는 쪽을 고치는 것이 사람들이 먼저 하는 일입니다. 보통 주의가 필요한 쪽은 다른 하나입니다.

코드는 시간의 함수이지 비밀번호가 아닙니다

TOTP는 공유 비밀과 현재 시간에서 유도한 카운터를 사용해 둘 다 키가 있는 해시에 통과시키고 결과를 짧은 십진 문자열로 자릅니다. 규격은 RFC 6238 에 정의되어 있습니다. 카운터는 시간 단계마다 진행되며 보통 삼십 초이므로, 단계가 바뀌면 같은 비밀도 새 값을 만듭니다. 검증자는 현재 단계의 기대값을 다시 계산해 비교하는데, 여기에는 같은 비밀과 '지금'에 대한 같은 인식이 필요합니다. 비밀번호는 변경할 때까지 재사용할 수 있지만, 이 값은 클라이언트가 볼 수 없는 경계에서 만료됩니다.

그래서 한 코드는 통하고 다음 코드는 몇 순간 뒤 실패합니다. 채팅 메시지에서 코드를 복사하는 습관이 그렇게 믿기 어려운 이유도 여기에 있습니다.

두 시계가 일치해야 하며 하나로는 충분하지 않습니다

등록 자료가 첫 번째 공유물이고 시간이 두 번째입니다. 비밀이 맞더라도 시계 차이가 허용 창을 넘으면 생성된 모든 값이 잘못된 단계에 떨어져 거부됩니다. 시계가 일치하더라도 비밀이 틀리면 모든 값이 거부되며, 두 실패 모두 로그인 화면에서는 똑같아 보입니다. 둘을 구분하려면 계정 항목과 시간을 따로, 이 순서로 확인해야 합니다.

검증자의 시계도 기기 시계만큼 의심할 가치가 있습니다. 시간 원천이 고장 난 서버는 모든 사용자의 올바른 코드를 한꺼번에 거부하며, 보고서에는 드리프트가 아니라 갑작스러운 장애로 적힐 것입니다. 이 패턴을 알아두어야 합니다. 고쳐야 할 곳은 서버이지 수백 대의 휴대폰이 아니기 때문입니다.

창을 넓히는 대가

구현은 작은 차이를 흡수하려고 인접 단계의 코드를 받아들일 수 있으며, 그 허용은 무료 조정이 아니라 설계 결정입니다. 단계를 하나 늘릴 때마다 어느 순간에 유효한 값의 수가 늘어나며, 그것은 추측하는 공격자가 바로 선호하는 상황입니다. 재사용 규칙도 같은 이유로 중요합니다. 두 번 받아들인 코드는 유용하게 유출된 코드입니다. 불만을 잠재우려고 창을 넓히는 관리자는 실제 속성을 겉모습 개선과 맞바꾸는 것입니다.

시계를 고치는 것이 수리이고, 창을 넓히는 것은 진정제입니다. 전자는 원인을 없애고 후자는 그것을 숨깁니다.

다시 시도하기 전에 두 시계를 모두 확인하십시오

기기와 서버에서 시간을 읽고, 표시된 현지 시간이 아니라 차이를 비교하십시오. 아래 명령은 리눅스 호스트나 서버에서 동기화 상태와 원시 UTC 시각을 보여 줍니다. 어떤 인증 설정이든 바꾸기 전에 양쪽에서 실행하십시오.

timedatectl
date -u +'%Y-%m-%dT%H:%M:%SZ'
chronyc tracking
timedatectl timesync-status

초 단위로 측정한 차이는 무해하고, 분 단위 차이는 거부를 설명하며, 동기화 원천을 전혀 보고하지 않는 시스템은 둘 다의 후보입니다. 두 UTC 타임스탬프를 직접 비교하십시오. 현지 시간에는 알고리즘이 결코 보지 못하는 시간대가 포함되기 때문입니다. 차이가 작은데도 코드가 계속 실패하면 시계 조정을 멈추고 대신 등록 항목을 점검하십시오. 잠긴 계정에 대한 반복 시도는 원래 결함을 관찰하기 어렵게 만듭니다.

거부 시각, 차이, 오류 문구를 기록하고 공유 비밀은 거기서 빼십시오. 지원팀은 처음 세 가지로 작업할 수 있으며 네 번째는 절대 보아서는 안 됩니다.

관련 글