1. 좀비 군단의 묻지마 폭행
여러분이 홍대에서 가장 인기 있는 클럽을 운영한다고 상상해보세요. 어느 날 경쟁 업체가 알바생 1,000명을 고용해서 클럽 입구에 줄을 세웁니다. 이들은 들어갈 생각도 없고, 입장료 낼 돈도 없습니다. 그저 입구 막고 서서 "들어갈까 말까?" 고민하는 척만 합니다. 진짜 손님들은 그 긴 줄을 보고 포기하고 돌아갑니다. 클럽 안은 텅 비었지만, 입구는 마비되었습니다.
이것이 바로 DDoS (Distributed Denial of Service, 분산 서비스 거부) 공격입니다. 해커는 전 세계에 퍼져 있는 감염된 PC, IoT 기기(좀비 PC, 봇넷) 수만 대에 명령을 내려 특정 서버를 향해 접속을 시도하게 합니다. 서버는 이 엄청난 요청을 처리하려다가 결국 CPU가 100%를 찍고 뻗어버리게 되죠.
2. 공격의 유형 - 무식하게 때리기 vs 지능적으로 괴롭히기
DDoS 공격은 네트워크 계층에 따라 크게 두 가지로 나뉩니다.
2.1. L3/L4 공격 (Volumetric Attack) - "무식하게 때리기"
네트워크 대역폭을 고갈시키거나 서버의 연결 테이블을 꽉 채우는 방식입니다.
- UDP Flooding: UDP는 연결 확인 과정이 없으므로, 그냥 엄청난 양의 데이터를 묻지마 전송합니다. 대역폭이 꽉 차서 정상 패킷이 못 들어옵니다. 공격자는 IP를 변조(Spoofing)하여 추적을 어렵게 만듭니다.
- TCP SYN Flood: TCP의 3-Way Handshake 취약점을 이용합니다. 공격자는
SYN(접속 요청)만 보내고ACK(확인)를 안 보냅니다. 서버는 "어? 응답이 왜 안오지?" 하고 기다리며 메모리(Backlog Queue)를 계속 잡고 있습니다. 결국 대기열이 꽉 차서 새로운 연결을 못 받게 됩니다 (앞서 말한 클럽 입구 막기 예시). - ICMP Flood (Ping Flood): 엄청난 양의 Ping을 보내서 서버가 응답하느라 바쁘게 만듭니다. 요즘은 잘 안 통하지만 고전적인 방식입니다.
2.2. L7 공격 (Application Layer Attack) - "지능적으로 괴롭히기"
웹 애플리케이션의 취약점을 노립니다. 트래픽 양은 많지 않지만 서버 리소스를 엄청나게 잡아먹습니다.
- Http Get Flood: 무거운 페이지(DB 조회가 많은 검색 결과, 이미지 처리 등)만 골라서 계속 새로고침합니다. 일반적인 웹 서핑인 척 위장해서 들어오기 때문에 구별하기 어렵습니다.
- Slowloris: 아주 천천히 요청을 보냅니다. 헤더를 1초에 한 글자씩 보냅니다.
Connection: Keep-Alive... (10초 대기) ...User-Agent: Mozilla... 서버는 "아직 요청이 다 안 왔네?" 하고 연결을 끊지 못하고 계속 기다립니다. 아파치(Apache) 서버 같은 스레드 기반 서버가 여기에 아주 취약합니다. 스레드가 다 소진되어 뻗어버립니다.
3. 방어 전략 - 어떻게 막을 것인가?
DDoS를 완벽하게 막는 단 하나의 방법은 없습니다. 여러 겹의 방어막(Layered Defense)을 쳐야 합니다.
1단계 - 깡패에는 깡패로 (CDN) - Bandwidth
100Gbps 규모의 공격이 들어오는데 내 서버 회선이 1Gbps라면? 아무리 튜닝을 잘해도 물리적으로 막을 수 없습니다. 회선이 터지니까요. 이때는 Cloudflare, AWS CloudFront, Akamai 같은 CDN(Content Delivery Network)을 써야 합니다. 이들은 전 세계에 거대한 대역폭(수 Tbps)을 가지고 있어서, 공격 트래픽을 거대 댐처럼 흡수해버립니다. 이를 "Scrubbing Center"라고 합니다. 악성 트래픽은 걸러내고(Scrubbing), 깨끗한 트래픽만 우리 서버(Origin)로 보내줍니다.
2단계 - 분산 (Anycast Network) - Dilution
Anycast는 "가장 가까운 놈이 받는다"는 라우팅 기술입니다. 전 세계 50개 서버가 똑같은 IP 주소를 가집니다. 한국에서 공격하면 한국 서버로 가고, 미국에서 공격하면 미국 서버로 갑니다. 이렇게 하면 공격 트래픽이 한 곳으로 집중되지 않고 전 세계로 분산(Dilution)되어 버립니다. N빵 하는 거죠. 공격자는 특정 타겟을 집중 타격하기가 매우 어려워집니다.
3단계 - 문지기 세우기 (Rate Limiting)
"1초에 100번 이상 요청하는 IP는 차단한다" 같은 규칙을 설정합니다. Nginx 설정이나 API Gateway에서 쉽게 할 수 있습니다. 물론 해커가 IP를 수천 개로 분산하면(봇넷) 막기 어렵지만, 기본적인 스크립트 키디들은 걸러낼 수 있습니다.
- Token Bucket 알고리즘: 일정 속도로 토큰을 채우고, 요청이 올 때마다 토큰을 씁니다. 토큰이 없으면 요청을 버립니다.
- Leaky Bucket 알고리즘: 구멍 뚫린 양동이처럼, 요청이 아무리 많이 들어와도 일정한 속도로만 처리합니다.
4단계 - 까다로운 검문 (WAF & Challenge)
L7 공격을 막기 위해 패킷 내부를 들여다봅니다. "SQL Injection 패턴이 있나?", "User-Agent가 이상한가?", "헤더 구조가 비정상인가?" Cloudflare 같은 서비스는 의심스러운 요청에 대해 "JS Challenge"를 보냅니다. (브라우저인지 봇인지 확인하는 간단한 계산 문제). 봇은 자바스크립트를 실행하지 못하는 경우가 많아서 여기서 걸러집니다.
4. 개발자가 할 수 있는 일
DDoS 방어는 주로 인프라/네트워크 엔지니어의 영역이지만, 개발자도 할 수 있는 일이 있습니다.
- 비싼 쿼리 최적화: DB에 부하를 많이 주는 API를 방치하지 마세요. 거기가 공격 타겟이 됩니다. 인덱스를 잘 태우고, N+1 문제를 해결하세요.
- 캐싱 적극 활용: Redis 등을 이용해 DB까지 요청이 가지 않게 막으세요. 정적 파일은 무조건 CDN에 올리세요.
- Circuit Breaker: 외부 서비스가 터졌을 때 내 서버까지 같이 죽지 않도록 안전장치를 거세요. 장애 전파(Cascading Failure)를 막아야 합니다.
- Timeouts 설정: Slowloris 공격을 막으려면 웹 서버의 타임아웃 설정을 짧게 잡아야 합니다. 요청이 너무 오래 걸리면 과감하게 끊어버리세요.
- IP 노출 금지: 클라우드플레어를 쓴다면 원본 서버(Origin)의 실제 IP가 노출되지 않도록 조심하세요. 해커가 실제 IP를 알아내면 CDN을 우회해서 직공(Direct Attack)을 때립니다. 방화벽(Security Group)에서 CDN의 IP 대역만 허용하세요.
6. 실제 사례 - GitHub 1.35 Tbps 공격 (2018)
DDoS의 무서움을 보여주는 유명한 사건이 있습니다. 2018년 2월, GitHub는 역사상 최대 규모인 1.35 Tbps의 트래픽 폭탄을 맞았습니다. 해커는 Memcached 증폭 공격(Amplification Attack)을 사용했습니다. 전 세계에 보안 설정이 안 된 Memcached 서버 10만 대에 작은 패킷을 보내고, 그 응답이 GitHub로 5만 배 뻥튀기되어 날아가게 만든 것입니다.
GitHub는 어떻게 살았을까요? 공격 시작 10분 만에 Akamai Prolexic 서비스로 트래픽을 우회시켰습니다. Akamai는 이 거대한 트래픽을 전 세계 스크러빙 센터에서 흡수해버렸고, GitHub는 20분 만에 정상화되었습니다. 돈(Enterprise CDN)이 좋긴 좋다는 걸 보여준 사례입니다.
7. 마무리 - 보안은 비용이다
DDoS 방어 시스템을 구축하는 건 돈이 듭니다. Cloudflare Enterprise 플랜은 비쌉니다. 하지만 서비스가 24시간 동안 다운되었을 때의 손실액(매출 하락, 신뢰도 추락, 고객 이탈)과 비교해보세요. 스타트업 초기에는 기본 방어(AWS Shield Basic 등)로 버티다가, 서비스가 커지고 유명해지면 "아, 이제 나도 공격받을 급이 되었구나" 생각하고 본격적인 보안 투자를 시작하면 됩니다.
보안에 절대 안전지대는 없습니다. 창과 방패의 끝없는 싸움일 뿐입니다. 우리는 방패를 계속 덧대고 업그레이드할 뿐입니다.