“타이밍 공격”, 서버 응답 시간만 보고 비밀번호를 추측한다고?
들어가며
웹 서비스를 운영하다 보면 보통 보안 정보는 직접 노출되지 않도록 관리합니다.
비밀번호는 암호화하거나 해시해서 저장하고, API 응답에도 민감한 정보를 넣지 않습니다.
그런데 여기서 한 가지 재미있는 문제가 있습니다.
화면에 아무 정보도 보여주지 않았는데도 공격자가 정보를 알아낼 수 있다면 어떨까요?
바로 이런 상황에서 등장하는 것이 타이밍 공격(Timing Attack)입니다.
타이밍 공격은 프로그램이 특정 작업을 처리하는 데 걸리는 아주 작은 시간 차이를 반복해서 측정하고, 그 결과를 분석해 비밀번호나 암호 키와 같은 민감한 정보를 추론하는 공격 방식입니다.
즉, 서버가 비밀 정보를 직접 알려주는 것이 아닙니다.
서버가 일하는 데 걸린 시간이 힌트가 되는 것입니다.
타이밍 공격이란?
타이밍 공격은 대표적인 사이드 채널 공격(Side-Channel Attack) 중 하나입니다.
사이드 채널 공격은 프로그램이 정상적인 기능을 수행하면서 외부로 노출하는 부수적인 정보를 이용하는 공격을 의미합니다.
예를 들면 다음과 같은 정보가 될 수 있습니다.
실행 시간
CPU 사용량
메모리 사용 패턴
전력 소비량
캐시 동작
그중 타이밍 공격은 말 그대로 시간(Time)을 이용합니다.
일반적인 공격이 프로그램의 결과값을 직접 분석한다면 타이밍 공격은 결과뿐만 아니라 “얼마나 빨리 처리됐는가”를 관찰합니다.
아주 간단한 예를 들어보자
비밀번호를 비교하는 프로그램이 있다고 생각해보겠습니다.
사용자가 입력한 비밀번호와 서버에 저장된 값을 한 글자씩 비교한다고 가정해보겠습니다.
입력값: ABCD
정답값: ABCD
첫 번째 문자부터 차례대로 비교하다가 틀린 문자가 발견되면 바로 종료하는 방식입니다.
A → 일치
B → 일치
C → 일치
D → 일치
이번에는 다음과 같이 입력했다고 해보겠습니다.
ABXD
비교 결과는 다음과 같습니다.
A → 일치
B → 일치
X → 불일치 → 종료
겉으로 보면 그냥 “비밀번호가 틀렸습니다”라는 결과만 반환하면 됩니다.
그런데 만약 일치하는 문자가 많을수록 비교 작업이 조금 더 오래 걸린다면 이야기가 달라집니다.
첫 번째 문자부터 틀림 → 짧은 시간
두 번째 문자까지 일치 → 조금 더 긴 시간
세 번째 문자까지 일치 → 더 긴 시간
공격자가 이런 요청을 수없이 반복하고 평균 응답 시간을 측정한다면 어떤 입력이 조금 더 오래 처리되는지를 통계적으로 분석할 수 있습니다.
이것이 타이밍 공격의 기본적인 아이디어입니다.
실제 시간 차이는 아주 작을 수 있다
여기서 오해하면 안 되는 부분이 있습니다.
타이밍 공격은 보통
1초
2초
3초
처럼 눈에 띄는 차이를 이용하는 것이 아닙니다.
실제로는 매우 작은 차이를 측정하는 상황을 생각해야 합니다.
1.201 ms
1.205 ms
1.203 ms
처럼 사람이 화면을 보면서 구별하기 어려운 수준의 차이일 수도 있습니다.
그래서 한두 번 요청해보고 “응답 시간이 비슷하니까 괜찮겠네”라고 판단하는 것은 적절하지 않습니다.
네트워크 지연이나 서버 부하처럼 다른 요인도 존재하기 때문입니다.
공격자는 여러 번 측정한 결과를 통계적으로 분석해 미세한 차이를 찾아낼 수 있습니다.
문자열 비교에서 특히 주의할 점
타이밍 공격을 설명할 때 흔히 등장하는 것이 문자열 비교입니다.
개발자가 직접 문자열을 비교하는 로직을 만든다고 생각해보겠습니다.
boolean check(String input, String secret) {
for (int i = 0; i < secret.length(); i++) {
if (input.charAt(i) != secret.charAt(i)) {
return false;
}
}
return true;
}
이 코드는 일반적인 문자열 비교 방식처럼 보입니다.
하지만 앞부분이 많이 일치할수록 더 많은 비교를 수행한 다음 종료하게 됩니다.
개념적으로는 다음과 같은 차이가 생길 수 있습니다.
X → 첫 글자에서 종료
A → 첫 글자 비교 후 종료
AB → 두 글자 비교 후 종료
ABC → 세 글자 비교 후 종료
물론 실제 시스템에서는 컴파일러 최적화, CPU 캐시, 네트워크 지연, 서버 부하 등 수많은 요소가 함께 작용하기 때문에 이런 차이가 그대로 측정된다고 단순하게 생각해서는 안 됩니다.
하지만 비밀값을 비교하는 코드에서 입력값에 따라 처리 시간이 달라지는 구조를 만들지 않는 것은 중요한 보안 원칙입니다.
그래서 Constant-Time 비교가 중요하다
타이밍 공격을 방어하기 위해 암호학에서는 Constant-Time(상수 시간) 비교라는 개념을 사용합니다.
여기서 말하는 상수 시간은 실제 프로그램이 언제나 정확히 똑같은 시간을 소비한다는 의미로 받아들이면 안 됩니다.
현실의 컴퓨터에서는 완전히 동일한 실행 시간을 보장하기 어렵습니다.
중요한 의미는 비밀값의 내용에 따라 실행 시간이 크게 달라지는 구조를 피하는 것입니다.
특히 암호 키, 인증 토큰, MAC 값 등의 비밀 데이터를 비교할 때 일반적인 조기 종료 방식의 문자열 비교 대신 보안 목적으로 설계된 constant-time 비교 함수를 사용하는 것이 좋습니다.
예를 들어 Java 환경에서는 보안이 중요한 바이트 배열 비교에 MessageDigest.isEqual() 같은 API를 고려할 수 있습니다.
boolean result =
MessageDigest.isEqual(expected, actual);
이런 식으로 검증 목적에 맞는 보안 API를 사용하는 것이 직접 비교 로직을 구현하는 것보다 안전한 접근입니다.
비밀번호 해시를 사용하면 끝일까?
비밀번호를 평문으로 저장하지 않고 안전한 비밀번호 해시 함수를 사용하는 것은 기본적인 보안 수칙입니다.
하지만 그렇다고 타이밍 공격과 관련된 모든 문제가 사라지는 것은 아닙니다.
예를 들어 로그인 과정에서
사용자 존재 여부 확인
↓
비밀번호 검증
같은 로직이 있고, 존재하는 사용자와 존재하지 않는 사용자의 처리 과정이 지나치게 다르다면 응답 시간의 차이가 발생할 수 있습니다.
공격자가 여러 계정명을 대상으로 응답 시간을 비교하면 특정 계정이 실제로 존재하는지 추론하려는 시도가 가능해질 수 있습니다.
이런 문제는 비밀번호 자체를 알아내는 것과는 다른 문제지만, 계정 정보 노출이라는 측면에서 보안상 의미가 있습니다.
타이밍 공격은 암호화에서만 발생하지 않는다
타이밍 공격이라고 하면 흔히 암호 알고리즘부터 떠올리기 쉽습니다.
하지만 실제로는 인증이나 권한 확인처럼 비밀 정보에 따라 처리 흐름이 달라지는 모든 코드에서 타이밍 차이를 고려할 수 있습니다.
예를 들어 다음과 같은 영역입니다.
비밀번호 검증
API Key 비교
인증 토큰 검증
암호화 키 비교
서명 검증
MAC 비교
민감한 문자열 비교
특히 인증과 암호 관련 코드는 일반적인 비즈니스 로직보다 보안 API를 사용하는 것이 중요합니다.
타이밍 공격을 어렵게 만드는 방법
타이밍 공격에 대응할 때는 한 가지 방법만 생각하기보다는 여러 방어 방법을 함께 적용하는 것이 좋습니다.
1. Constant-Time 비교 사용
비밀값을 비교할 때 직접 == 또는 일반 문자열 비교 로직을 구현하기보다 해당 목적에 맞는 보안 라이브러리의 비교 함수를 사용하는 것이 좋습니다.
2. 조기 종료를 주의한다
비밀값의 앞부분이 틀렸다고 즉시 종료하는 방식은 상황에 따라 처리 시간 차이를 만들 수 있습니다.
특히 암호 키나 인증 토큰처럼 외부에서 추측할 수 있는 값과 비밀값을 비교하는 코드는 더욱 주의해야 합니다.
3. 인증 실패 응답을 일관되게 만든다
사용자가 존재하는 경우와 존재하지 않는 경우에 처리 과정이나 응답 특성이 지나치게 다르지 않도록 설계할 필요가 있습니다.
4. 반복 요청을 어렵게 만든다
Rate Limiting이나 로그인 시도 제한 등을 적용하면 공격자가 수많은 요청을 보내 측정하는 것을 어렵게 만들 수 있습니다.
5. 보안 라이브러리를 활용한다
암호화나 인증 관련 코드를 직접 구현하기보다는 검증된 보안 라이브러리와 API를 사용하는 것이 좋습니다.
응답 시간을 일부러 똑같이 만들면 될까?
여기서도 흔히 나오는 질문이 있습니다.
“그럼 로그인 요청마다 무조건 1초씩 기다리게 하면 되는 것 아닌가?”
단순하게 생각하면 그럴듯하지만 좋은 해결책이라고 보기는 어렵습니다.
서버가 의도적으로 모든 요청을 지연시키면 정상적인 사용자까지 영향을 받게 됩니다.
또한 실제 시스템의 시간 차이는 하나의 코드에서만 발생하는 것이 아니라 네트워크, 데이터베이스, 캐시, CPU 등 여러 요소의 영향을 받습니다.
따라서 단순히 sleep() 같은 방식으로 응답 시간을 맞추는 것보다는 민감한 값을 처리하는 핵심 로직 자체에서 입력값에 따른 시간 차이를 줄이고, 반복 측정을 어렵게 만드는 방식을 함께 사용하는 것이 현실적인 접근입니다.
개발자가 코드를 볼 때 확인할 부분
기존 프로젝트에서 타이밍 공격 가능성을 점검한다면 암호나 인증 관련 코드를 먼저 찾아보는 것이 좋습니다.
예를 들어 다음과 같은 코드입니다.
비밀번호 비교
토큰 비교
API Key 비교
서명 검증
Hash 비교
MAC 검증
그리고 다음과 같은 패턴이 있는지 살펴볼 수 있습니다.
if (input.equals(secret)) {
// 인증 성공
}
이 코드 한 줄만 보고 취약점이라고 단정할 수는 없습니다.
중요한 것은 무엇을 비교하는지입니다.
일반적인 사용자 이름이나 공개 데이터라면 타이밍 공격의 의미가 크지 않을 수 있습니다.
반면 외부에서 추측할 수 있는 입력값과 암호 키, 인증 토큰 같은 비밀값을 비교한다면 constant-time 비교가 필요한지 검토해야 합니다.
마무리
타이밍 공격은 처음 들으면 상당히 복잡한 해킹 기법처럼 느껴집니다.
하지만 원리는 생각보다 단순합니다.
“프로그램이 얼마나 오래 걸렸는지에서 정보를 얻어내는 공격”입니다.
서버가 비밀번호를 직접 알려주지 않아도 처리 시간에 미세한 차이가 있다면 공격자가 반복적인 측정을 통해 그 차이를 분석할 수 있습니다.
그래서 암호화나 인증과 관련된 코드를 작성할 때는 단순히 결과값만 확인해서는 부족합니다.
무엇을 반환하는가?
+
얼마나 오래 걸리는가?
+
입력에 따라 처리 시간이 달라지는가?
이 세 가지를 함께 생각해볼 필요가 있습니다.
특히 비밀번호, 인증 토큰, API Key, 암호 키처럼 민감한 데이터를 비교하는 코드라면 검증된 보안 라이브러리를 사용하고, 필요한 경우 Constant-Time 비교 방식을 적용하는 것이 좋습니다.
보안에서는 “화면에 비밀값을 출력하지 않았으니 안전하다”라고 단순하게 생각하면 안 됩니다.
프로그램이 무심코 흘리는 작은 시간 차이도 공격자에게는 정보가 될 수 있습니다.
그게 바로 타이밍 공격을 알아둬야 하는 이유입니다.
홍TV



![Eclipse에서 갑자기 "[m2e] Lifecycle Mapping" 오류? Maven 프로젝트가 빨간 줄 뜨는 이유 4 Eclipse에서 갑자기 [m2e] Lifecycle Mapping 오류? Maven 프로젝트가 빨간 줄 뜨는 진짜 이유](https://hongtv.co.kr/wp-content/uploads/2026/09/62edeb0f-aaf8-42cc-af31-1a91e54bc187-300x200.png)
댓글 0
첫 댓글을 남겨보세요.