HTTP Flood 공격 방어를 위한 요청 속도 제한 알고리즘의 효율성 비교

📅 1월 30, 2026 👤 Erika Wolfe

증상 진단: 웹 서버가 갑자기 응답 불능 상태인가요?

웹 애플리케이션이 평소와 다르게 극도로 느려지거나, 아예 접속이 불가능해졌으며, 서버 모니터링 도구에서 CPU 사용률과 네트워크 트래픽이 정상 범위를 크게 초과하는 패턴을 관찰하고 계신다면, 이는 HTTP Flood 공격을 의심해야 하는 명확한 증상입니다. 공격자는 수천. 수만 개의 가짜 http 요청(주로 get 또는 post)을 초당 몰아쳐 애플리케이션 계층에서 서버 리소스를 고갈시키는 것을 목표로 합니다. 단순한 대역폭 공격과 달리, 이 공격은 정상적인 트래픽처럼 보이는 패킷을 사용하므로 기존의 방화벽만으로는 탐지와 차단이 어려운 경우가 많습니다.

원인 분석: 애플리케이션 계층 자원 고갈

HTTP Flood 공격의 핵심 메커니즘은 웹 서버나 애플리케이션 서버(WAS)의 처리 한계를 의도적으로 초과시키는 데 있습니다. 각 HTTP 요청은 서버 내에서 연결 수락, 세션 관리, 데이터베이스 쿼리 실행, 동적 페이지 생성 등 일련의 비용이 드는 프로세스를 트리거합니다. 공격자는 이 프로세스를 최소한의 비용으로 대량 생성하여 서버의 가용 연결 수(예: TCP Backlog), 스레드 풀, 메모리, DB 커넥션 등을 순식간에 소진시킵니다. 결과적으로 합법적인 사용자의 요청은 처리 리소스를 할당받지 못하고 큐에서 대기하거나 드롭되어 서비스 장애로 이어집니다.

해결 방법: 요청 속도 제한 알고리즘 비교 및 구현

HTTP Flood 공격을 효과적으로 완화하기 위한 핵심 전략은 요청 속도 제한(Rate Limiting)입니다. 이는 클라이언트(IP, 세션, 사용자 ID 등)별로 일정 시간 동안 허용할 최대 요청 수를 정의하고, 이를 초과하는 요청을 지연시키거나 거부하는 메커니즘입니다. 다양한 알고리즘이 존재하며, 각각의 효율성(처리 속도, 메모리 사용량), 공정성, 구현 복잡도가 상이합니다. 적절한 알고리즘 선택은 시스템 규모와 요구사항에 따라 결정되어야 합니다.

주의사항: 본 가이드에서 제시하는 알고리즘 설정 변경은 운영 서버에 직접 적용하기 전에 반드시 스테이징 환경에서 충분히 테스트해야 합니다. 잘못된 설정은 정상 사용자의 접근을 차단하는 역효과를 낳을 수 있습니다, 또한, 알고리즘 구현 전에 웹 서버(nginx, apache) 또는 애플리케이션 프레임워크(spring, express) 레벨에서 제공하는 기본 rate limit 모듈의 활용을 먼저 검토하는 것이 현실적입니다.

Method 1: 토큰 버킷 알고리즘 – 유연한 버스트 트래픽 처리

토큰 버킷(Token Bucket) 알고리즘은 설정된 속도로 버킷에 토큰이 채워지고, 각 요청은 처리하기 위해 하나의 토큰을 소비하는 방식으로 작동합니다. 버킷에 토큰이 있으면 요청을 즉시 처리하며, 없으면 대기하거나 거부합니다. 버킷 크기가 허용하는 범위 내에서 짧은 버스트(Burst) 트래픽을 수용할 수 있어 실제 사용자 패턴을 모방한 공격에 대한 초기 대응에 유리합니다.

  1. 핵심 파라미터 정의: 버킷 크기(Burst Size, 최대 누적 토큰 수)와 토큰 충전률(Refill Rate, 초당 토큰 수)을 설정합니다. 예: burst=100 requests, rate=10 requests/second.
  2. 구현 로직: 클라이언트 키(예: IP 주소)별로 마지막 토큰 충전 시간과 현재 토큰 수를 저장합니다. 요청 도착 시, 경과 시간에 따른 토큰 충전량을 계산한 후, 토큰이 1개 이상이면 차감하고 요청을 허용합니다.
  3. 장단점 분석:
    • 장점: 제한된 버스트 트래픽을 허용하여 사용자 경험을 유지할 수 있음. 메모리 사용량이 비교적 적고(키별로 두 개의 값 저장), 계산 효율이 좋음.
    • 단점: 버스트 크기를 너무 크게 설정하면 공격자가 이 간격을 이용하여 피해를 줄 수 있음. 장기간의 고속 공격에는 취약할 수 있습니다.

Method 2: 고정 윈도우 카운터 – 단순하고 직관적인 구현

고정 윈도우 카운터(Fixed Window Counter) 알고리즘은 타임라인을 고정된 크기(예: 1초, 1분)의 윈도우로 나누고, 각 윈도우 내에서 클라이언트의 요청 수를 카운트합니다. 카운트가 임계값을 초과하면 해당 윈도우가 끝날 때까지 모든 추가 요청을 거부합니다. 개념이 단순하여 구현과 이해가 쉽습니다.

  1. 핵심 파라미터 정의: 윈도우 크기(예: 60초)와 윈도우 당 최대 허용 요청 수(예: 100회)를 설정합니다.
  2. 구현 로직: 클라이언트 키와 현재 윈도우의 시작 타임스탬프를 조합한 키를 사용하여 카운터를 저장소(인메모리 DB 등)에 증가시킵니다. 현재 시간이 윈도우를 벗어나면 카운터가 리셋되는 새로운 윈도우가 시작됩니다.
  3. 장단점 분석:
    • 장점: 구현이 매우 간단하며, 메모리 효율이 좋을 수 있습니다.
    • 단점: 윈도우 경계에서 발생하는 버스트에 취약합니다. 예를 들어, 윈도우가 1분이고 제한이 100회일 때, 공격자가 첫 윈도우의 마지막 1초와 다음 윈도우의 첫 1초에 각각 100회의 요청을 보내면 1초 동안 실제로 200회의 요청이 처리될 수 있어 제한이 무력화됩니다.

Method 3: 슬라이딩 윈도우 로그 – 정밀한 제어와 공정성

슬라이딩 윈도우 로그(Sliding Window Log) 알고리즘은 각 요청의 타임스탬프를 로그에 기록합니다. 새로운 요청이 들어오면, 현재 시간에서 윈도우 크기(예: 1분)를 뺀 시점 이후의 타임스탬프만을 남기고 오래된 로그는 삭제합니다. 남아 있는 로그의 개수가 허용 한도를 초과하면 요청을 거부합니다. 이 방법은 가장 정확하게 시간 범위 내의 요청 수를 제한합니다.

  1. 핵심 파라미터 정의: 윈도우 크기와 허용 요청 수를 설정합니다. (고정 윈도우와 파라미터는 유사그럼에도 동작 방식이 근본적으로 다름)
  2. 구현 로직: 클라이언트 키별로 타임스탬프 리스트 또는 정렬 집합을 유지합니다. 요청 시 현재 타임스탬프를 삽입하고, 윈도우 범위 밖의 오래된 타임스탬프를 모두 제거한 후, 리스트의 길이를 확인하여 허용 여부를 결정합니다.
  3. 장단점 분석:
    • 장점: 시간에 따른 정확한 제어가 가능하며, 고정 윈도우의 경계 버스트 문제를 완전히 해결합니다. 공정성과 정밀도가 가장 높습니다.
    • 단점: 모든 요청의 타임스탬프를 저장해야 하므로 메모리 사용량이 클라이언트 수와 요청량에 비례하여 증가할 수 있습니다. 로그 정리 작업에 추가적인 CPU 오버헤드가 발생합니다.

Method 4: 슬라이딩 윈도우 카운터 – 효율성과 정확성의 절충

슬라이딩 윈도우 카운터(Sliding Window Counter)는 고정 윈도우의 효율성과 슬라이딩 윈도우 로그의 정확성을 절충한 알고리즘입니다. 현재 윈도우와 이전 윈도우의 카운트를 가중치를 두어 계산하여 근사적인 슬라이딩 윈도우 카운트를 추정합니다. Redis의 INCR 명령어와 만료 시간 설정을 활용한 구현이 일반적입니다.

  1. 핵심 파라미터 정의: 고정 윈도우 카운터와 동일하게 윈도우 크기와 허용 요청 수를 설정합니다.
  2. 구현 로직 (근사치 계산): 1분 윈도우, 100회 제한 시, 현재 1분(0초~60초)의 카운트와 이전 1분(60초 전~120초 전)의 카운트를 조합합니다. 예를 들어, 현재 시각이 1분 30초라면, 현재 윈도우(30초~90초)의 예상 요청 수 = 이전 윈도우 카운트 * (남은 윈도우 비율) + 현재 윈도우 카운트. 이 값이 한도를 초과하면 요청을 거부합니다.
  3. 장단점 분석:
    • 장점: 슬라이딩 윈도우 로그보다 메모리 효율이 훨씬 뛰어나며(키별로 소량의 카운터 값만 저장). 고정 윈도우보다는 정확한 제어가 가능합니다.
    • 단점: 완벽한 정확성을 보장하지는 않으며, 가중치 계산 로직이 추가됩니다.

효율성 비교 및 선택 가이드

상황에 맞는 알고리즘 선택은 시스템의 규모, 트래픽 패턴, 허용 오차 범위에 따라 달라집니다. 다음은 실무 관점에서의 비교표입니다.

  • 처리 속도 및 확장성: 토큰 버킷과 슬라이딩 윈도우 카운터가 분산 환경(Redis 활용)에서 확장하기에 가장 적합합니다. 고정 윈도우도 단순함으로 인해 빠르지만 경계 버스트 리스크가 있습니다.
  • 메모리 효율성: 고정 윈도우, 토큰 버킷, 슬라이딩 윈도우 카운터는 모두 O(1) 공간 복잡도를 지향합니다. 반면, 슬라이딩 윈도우 로그는 O(N)의 공간이 필요하여 대규모 공격 시 메모리 부하가 심각할 수 있습니다.
  • 정확도와 공정성: 슬라이딩 윈도우 로그가 가장 정확합니다, 토큰 버킷은 버스트 허용으로 인해 다소 느슨한 제어가 가능하며, 이는 사용자 경험 측면에서는 장점이 될 수 있습니다.
  • 실전 추천 시나리오:
    • api 게이트웨이 / cdn 엣지: 낮은 지연 시간과 높은 처리량이 필수이므로 토큰 버킷 또는 슬라이딩 윈도우 카운터를 사용한 분산 rate limiting을 구현해야 합니다.
    • 로그인, otp 전송 등 중요 엔드포인트: 정확한 제어가 필요하며 버스트를 허용해서는 안 되는 경우, 슬라이딩 윈도우 로그를 적용하는 것을 고려해야 합니다. 성능이 걱정된다면 근사치 알고리즘인 슬라이딩 윈도우 카운터로 대체할 수 있습니다.
    • 간단한 블로그나 소규모 애플리케이션: Nginx의 limit_req 모듈(토큰 버킷 알고리즘 사용)과 같은 웹 서버 수준의 기본 제한 기능으로도 충분한 효과를 볼 수 있습니다.

전문가 팁: 다층 방어 체계 구축 단일 알고리즘에 의존하기보다는 다층 방어를 구성하는 것이 실제 공격을 효과적으로 막는 길입니다. 1) 네트워크/인프라 층: DDoS 방어 서비스(AWS Shield, Cloudflare)를 활용해 대역폭 공격을 흡수합니다. 2) 웹 서버 층: Nginx의 limit_req_zone으로 기본적인 IP별 속도 제한을 설정합니다. 3) 애플리케이션 층: 본문에서 분석한 알고리즘을 사용하여 비즈니스 로직에 맞는 세밀한 제어(예: 사용자 ID별 API 호출 제한)를 구현합니다. 4) 위협 인텔리전스: 지속적으로 이상 패턴을 학습하고, 공격 IP 리스트를 실시간으로 차단 목록에 업데이트하는 자동화 프로세스를 갖추는 것이 장기적인 방어 역량을 결정합니다. 모든 로그는 중앙 집중화되어 분석 가능한 상태로 보관되어야 하며, 이는 공격 경로 추적과 사후 대응의 근거가 됩니다.

관련 글