blog.stackframe.dev

hash_equals() 타이밍 공격에 안전한 해쉬 비교 함수

PHP 내장 함수들을 둘러보다 hash_equals()라는 재미있는 함수를 발견했다. 설명을 보니 타이밍 공격을 방지하기 위해 만들어진 함수인 것 같다.

타이밍 공격이란 처리되는 시간을 분석해서 해킹하는 기법을 말한다. 예를 들어 두 개의 해쉬값이 동일한지 검증한다고 하면 어떻게 할까. 맨 앞의 바이트부터 차례대로 동일한지 비교하고 다른게 발생한다면 그 자리에서 바로 틀리다고 반환하도록 할 것이다. 여기서 해킹을 시도할 수 있는 미세한 틈이 발생한다. 첫번째 바이트부터 동일하지 않은 해쉬값과 두번째 바이트부터 동일하지 않은 해쉬값을 비교해보면 후자가 더 많은 명령어를 수행하고 CPU 사이클도 더 사용한다. 이걸 측정하여 공격자는 차근차근 비교되는 해쉬값을 알아낼 수 있다. CPU 멜트다운 취약점도 일종의 타이밍 공격이다. 캐시에 들어있는 응답은 빠르게 반환된다는 점을 이용하여 데이터를 불러오는데 걸리는 시간을 측정하여 정보를 뽑아내기 때문이다.

이걸 방지하기 위해 hash_equals() 함수는 맨 먼저 두 문자열의 길이가 같은지 확인하고 처음부터 끝까지 비교를 한 뒤에 결과를 반환한다. 소스코드를 보면 모든 바이트의 XOR 연산 결과를 OR로 누적하고 마지막에 결과값이 0인지 비교한다:

/* This is security sensitive code. Do not optimize this for speed. */
for (j = 0; j < Z_STRLEN_P(known_zval); j++) {
    result |= known_str[j] ^ user_str[j];
}

RETURN_BOOL(0 == result);

PHP 메뉴얼에도 나와있지만 두 문자열의 길이가 다르다면 바로 False를 반환한다. 이걸 사용하여 비교하는 값의 길이를 타이밍 공격으로 알아낼 수도 있다.

물론 웹이라는 특성 상 서버의 처리 속도와 응답시간에 변수가 너무나도 많아서 몇 CPU 사이클 정도의 미세한 시간을 클라이언트에서 정확하게 측정하는 것은 매우 어렵다. 로컬에서 실행하는게 아닌 이상 타이밍 공격은 사실상 불가능하다고 생각된다. 그래도 가능성이 있으면 최대한 배제하는게 안전하니 앞으로 비밀번호같은 해쉬값을 비교한다면 이 함수를 사용하는게 좋을 것이다.

댓글