기반 프리미티브
비교가 얼마나 걸렸는지도 비밀로 취급해 값을 한 바이트씩 복원당하는 통로를 막습니다.
규격 재현과 공격 거부를 이 페이지에서 직접 실행해 확인할 수 있습니다.
암호는 보통 정답을 모르면 열리지 않는다는 것을 보장하지만, 공격자가 얻는 정보는 결과값만이 아닙니다. 처리 시간·전력·캐시 상태 같은 부수 채널도 관찰 대상이며 시간은 네트워크 너머에서도 측정됩니다. 조기 종료 비교는 앞에서 몇 바이트가 일치했는지를 시간으로 흘리고, 그 한 가지 정보만으로 비밀값 전체가 복원됩니다.
결과값이 아니라 처리 과정의 부산물에서 정보가 새는 경로를 뜻합니다.
다른 바이트를 만나면 즉시 멈추는 비교가 일치한 접두사 길이를 노출합니다.
위치별로 후보를 시도해 전수 탐색을 길이에 비례하는 비용으로 바꿉니다.
32바이트 MAC을 전수 탐색하려면 10^77번쯤 시도해야 합니다. 그런데 비교가 조기 종료하면 공격자는 전체를 맞출 필요가 없습니다. 첫 바이트로 256가지를 시도해 가장 오래 걸리는 후보를 고르고, 그 자리를 확정한 뒤 다음 바이트로 넘어갑니다. 비용이 곱셈에서 덧셈으로 바뀌어 8,192번이면 끝납니다. 이 모듈의 공격 증명이 실제로 그 복원을 수행합니다.
타이밍을 실제로 측정하면 노이즈 때문에 결과가 흔들려 CI에서 간헐적으로 실패합니다. 증명이 가끔 빨개지면 증명으로서의 신뢰가 먼저 무너집니다. 그래서 비교가 들여다본 바이트 수를 관찰값으로 씁니다. 이 횟수는 실제 소요 시간에 비례하므로 공격의 구조는 그대로이고, 관찰 채널만 시간에서 횟수로 바꾼 것입니다.
Node의 timingSafeEqual은 길이가 다르면 예외를 던집니다. 그 예외를 그냥 삼키면 길이가 다르다는 사실이 다시 시간 차이로 새어 나갑니다. 이 모듈은 길이를 먼저 확인해 false로 끊되, 비교 대상이 MAC이나 digest처럼 고정 길이라는 전제를 명시합니다. 길이 자체가 비밀인 값에는 이 계약을 쓰면 안 됩니다.
아래 증명은 설명이 아니라 실제 실행입니다. 버튼을 누르면 이 서버에서 지금 계산합니다.
비교 결과는 같다·다르다 두 가지뿐이어야 한다 — 어느 위치에서 갈렸는지, 앞에서 몇 바이트가 맞았는지는 답의 일부가 아니라 비밀이다. 조기 종료 비교는 그 정보를 처리 시간으로 내보낸다.
표준이 고정한 test vector 를 그대로 실행합니다.
공격자는 비밀을 한 번도 보지 못한다. 오직 비교가 몇 바이트를 봤는지만 관찰한다. 그런데도 8바이트 MAC이 2,048번 시도로 완전히 복원된다 — 전수 탐색이었다면 10^19번이 필요했다. 상수 시간 비교로 바꾸면 이 통로가 닫힌다.
막아야 할 입력을 실제로 넣고 거부되는지 확인합니다.
규격 원문을 우선합니다. 사례는 이 주제가 실제로 어떻게 뚫렸는지를 다룬 자료입니다.
이 결함 유형의 표준 분류와 완화 방법.
네트워크 너머에서 타이밍으로 OpenSSL 개인키를 복원한 연구.
해시 비교 타이밍 하나로 부팅 서명 검증이 뚫린 사례.
Next step
검증 경로 전수를 훑어 남아 있는 일반 비교를 찾아내고 정적 검사로 재발을 막습니다.