6 분 소요

📦 DDoS 완전정복 6부작 — DDoS가 무엇이고 공격이 어떻게 분류되는지 큰 그림을 그리고(1편), 대역폭을 말려버리는 볼류메트릭·반사증폭 공격(2편), 연결 테이블을 터뜨리는 프로토콜·상태 고갈 공격(3편), 정상 요청처럼 굴며 서버를 태우는 애플리케이션 계층(L7) 공격(4편), 이 모든 걸 탐지하고 막아내는 방어·완화(5편), 그리고 세운 방어가 실제로 버티는지 내 사이트에서 합법적으로 검증하는 내성 테스트(6편)까지 이어집니다. 전체 6편.
  1. DDoS 완전정복 — 개념과 공격 지형도
  2. 볼류메트릭·반사증폭 공격 — 대역폭을 말려버리는 방법
  3. 프로토콜·상태 고갈 공격 — 연결 테이블을 터뜨리기
  4. 애플리케이션 계층(L7) 공격 — 정상 요청처럼 굴며 서버를 태우기
  5. DDoS 방어와 완화 — 탐지부터 스크러빙까지
  6. 내 사이트 DDoS 내성 테스트 — 합법적으로, 제대로지금 글

Summary

앞의 다섯 편에서 공격을 이해하고, 그 위에 계층별 방어를 쌓았어요. 그런데 방벽은 세워만 두면 절반짜리예요. 진짜 공격이 오기 전에 내가 먼저 두들겨서 어디서 무너지는지 확인해야 방어값을 제대로 맞출 수 있습니다. 이번 마지막 편은 “내 사이트의 DDoS 내성을 어떻게 합법적으로, 제대로 테스트하는가”예요. 시작하기 전에 분명히 해둘 게 있어요. 이 글은 부터(booter)·스트레서나 봇넷을 쓰지 않습니다. 그건 내 사이트를 대상으로 해도 불법이고, 남의 기기를 동원하는 범죄예요. 우리가 쓰는 건 내가 소유·승인한 자산에, 정식 부하 도구로 통제된 부하를 거는 방법뿐입니다.

💡 이 글에서 다루는 것

  • 테스트 전에 반드시 확보할 것 — 승인·격리·중단 기준
  • k6로 하는 L7 부하 테스트와 방어(rate limit·오토스케일) 발동 관찰
  • 폐쇄 랩에서 SYN cookie 같은 프로토콜 방어를 검증하는 법
  • 무엇을 측정하고 언제 “통과”로 볼 것인가
  • 결과를 방어 설정으로 되먹이는 재테스트 루프



1. 왜 방어만으로는 부족한가

방어 설정은 “이 정도면 되겠지”라는 가정 위에 서 있어요. rate limit을 초당 10요청으로 잡았는데 그게 정상 사용자에겐 너무 빡빡하진 않은지, 오토스케일이 트래픽 급증을 제때 따라오는지, SYN cookie가 실제로 켜져서 동작하는지는 두들겨보기 전에는 모릅니다.

내성 테스트의 목적은 공격을 재현하는 게 아니라, 내 방어가 예상대로 작동하는지 확인하는 데 있어요. 그래서 관점이 공격자와 정반대예요. 공격자는 “어떻게 뚫을까”를 보지만, 우리는 “어디가 먼저 휘고, 그때 방어가 켜지는가”를 봅니다. 측정하고, 튜닝하고, 다시 측정하는 루프가 핵심이에요.



2. 시작 전에 반드시 — 승인·격리·중단 기준

여기가 이 글에서 제일 중요한 부분이에요. 기술보다 앞서는 전제입니다.

🚨 “내 사이트니까 괜찮다”는 절반만 맞아요. 트래픽이 내 서버로 가더라도 중간의 통신사·클라우드·CDN 망을 거칩니다. AWS·GCP·Cloudflare는 전부 사전 승인 없는 부하/DoS 테스트를 약관으로 금지해요. 부터·스트레서·봇넷은 소유권과 무관하게 불법이고요. 아래 세 가지를 확보하기 전에는 아무것도 쏘지 마세요.

  • 승인 — 프로덕션을 대상으로 하려면 클라우드 사업자의 부하 테스트 승인 절차를 먼저 밟아요. AWS는 시뮬레이션/부하 테스트 정책이 있고, Cloudflare·Akamai도 자체 프로그램이 있습니다. 조직이라면 내부 승인(책임자 서명)과 테스트 창(window)도 합의해 두고요.
  • 격리 — 가능하면 프로덕션이 아니라 스테이징 복제본을 대상으로 합니다. 네트워크 계층 테스트는 인터넷에 안 나가는 폐쇄 랩(내가 소유한 VPC 서브넷·온프렘 세그먼트) 안에서만 해요. 트래픽이 남의 망을 타지 않게 가두는 게 원칙입니다.
  • 중단 기준 — 시작 전에 “언제 멈출지”를 정해둡니다. 실사용자 영향, 에러율 임계, 인접 시스템 이상 같은 킬 스위치 조건을 문서로 만들고, 한 사람이 상황을 지켜보며 즉시 중단할 수 있게 해요.

이 세 가지가 없으면 그건 테스트가 아니라 사고예요.



3. 테스트 계획 세우기

두들기기 전에 계획서 한 장을 씁니다. 거창할 필요는 없고, 아래 항목만 채우면 돼요.

  • 가설 — “rate limit이 초당 10요청에서 발동해 원본을 보호할 것이다” 처럼 검증할 명제를 한 줄로.
  • 범위 — 대상 호스트, 대상 엔드포인트, 제외 대상(결제·외부 연동 등 건드리면 안 되는 곳)을 명시.
  • 지표(SLI) — 무엇을 볼지. 아래 표처럼 몇 개만 딱 정합니다.
지표 무엇을 보나
p95 응답시간 사용자 체감 지연
에러율 5xx · 타임아웃 비율
처리 rps 초당 정상 처리량
자원 사용 CPU · 커넥션 · 워커
  • 부하 프로파일 — 얼마를 얼마 동안. 처음부터 최대로 때리지 말고 계단식(ramp) 으로 올려요.
  • 롤백 — 테스트가 끝나거나 중단되면 원상복구할 절차.

핵심은 “무너뜨리기”가 아니라 “어디서 무너지고, 그 직전에 방어가 켜지는가“를 재는 거예요.



4. L7 내성 테스트 — k6로 방어 발동 관찰

애플리케이션 계층은 정식 부하 도구로 아주 깔끔하게 검증할 수 있어요. 저는 k6 를 즐겨 쓰는데, 스크립트로 부하 프로파일과 통과 기준을 코드로 박아둘 수 있어서 재현이 쉽습니다. 이 블로그의 스트레스 테스트 시리즈에서 다룬 그 도구를, 이번엔 “방어가 켜지는지” 관점으로 씁니다.

아래는 내 스테이징 서버에 계단식으로 부하를 올리며, rate limit이 발동하는 지점을 관찰하는 최소 예시예요.

import http from 'k6/http';
import { check } from 'k6';

// 대상은 반드시 내가 소유·승인한 스테이징 호스트
const TARGET = 'https://staging.내도메인.example/login';

export const options = {
  stages: [
    { duration: '1m', target: 50 },   // 워밍업
    { duration: '2m', target: 200 },  // 계단식 증가
    { duration: '2m', target: 500 },  // 방어 발동 관찰 구간
    { duration: '1m', target: 0 },    // 쿨다운
  ],
  thresholds: {
    // 방어가 정상 트래픽을 지켜주는지에 대한 통과 기준
    http_req_duration: ['p(95)<800'],
    http_req_failed: ['rate<0.05'],
  },
};

export default function () {
  const res = http.get(TARGET);
  // 429(Too Many Requests)가 뜨는 지점 = rate limit 발동 확인
  check(res, {
    '정상 응답(2xx)': (r) => r.status >= 200 && r.status < 300,
    'rate limit 발동(429)': (r) => r.status === 429,
  });
}

여기서 보는 건 “서버가 죽었나”가 아니에요. 부하를 올릴 때 429가 언제부터 뜨는지, 그 순간에도 원본 서버의 CPU와 응답시간이 안전 범위에 머무는지를 봅니다. 429가 적절한 지점에서 뜨고 원본이 평온하면, rate limit 방어가 제대로 일하는 거예요. 반대로 방어가 안 켜진 채 p95가 치솟고 5xx가 늘면, 임계값을 조여야 한다는 신호입니다.

캐시 우회 내성도 같은 틀로 확인할 수 있어요. 요청 URL에 매번 다른 쿼리스트링을 붙여 캐시를 우회했을 때 원본이 견디는지, CDN 캐시 적중률이 어떻게 떨어지는지를 관찰하면 됩니다.



5. L4·프로토콜 내성 — 폐쇄 랩에서만

네트워크 계층 방어(SYN cookie, conntrack 튜닝)는 성격이 달라요. 이건 인터넷에 절대 내보내지 않고, 내가 소유한 폐쇄 랩(격리된 가상 네트워크나 랩 장비) 안에서만 확인합니다. 트래픽이 랩 밖으로 새지 않도록 네트워크를 가두는 게 대전제예요.

여기서 저는 부하 생성 자체보다 방어가 켜졌는지 관측하는 쪽에 무게를 둬요. 랩에서 트래픽 생성기(예: 트래픽 제너레이터 계열 도구)로 SYN 부하를 만든 뒤, 서버에서 이런 지표를 지켜봅니다.

# SYN cookie가 실제로 켜져 있는지 확인
sysctl net.ipv4.tcp_syncookies

# 커널이 SYN cookie를 발동한 횟수 (증가하면 방어가 일하는 중)
netstat -s | grep -i "syncookies sent"

# 소켓 상태 요약 — SYN_RECV(반쯤 열린 연결)가 쌓이는지
ss -s

# 연결 추적 테이블 사용량 (고갈 임박 여부)
sysctl net.netfilter.nf_conntrack_count net.netfilter.nf_conntrack_max

관찰 포인트는 명확해요. SYN 부하를 주는 동안 syncookies sent 카운터가 올라가고, SYN_RECV 가 backlog를 다 채우지 않고 흡수되며, 그 와중에도 정상 클라이언트가 랩 안에서 접속을 유지하면 방어가 작동한 겁니다. conntrack 카운트가 상한에 근접하면 테이블 크기나 타임아웃을 손봐야 한다는 뜻이고요.

⚠️ 이 단계는 “얼마나 세게 때리나”가 목적이 아니라 “방어 설정이 실제로 발동하나”를 보는 거예요. 그래서 인터넷 경로가 조금이라도 끼면 안 됩니다. 랩 밖으로 나가는 순간 그건 검증이 아니라 남의 망을 치는 일이 돼요.



6. 관측과 판정 — 언제 “통과”인가

테스트는 그래프 없이는 의미가 없어요. 부하를 거는 쪽(k6 요약)과 받는 쪽(서버·방어 계층)의 지표를 같은 시간축에 놓고 봐야 합니다. 보통 Grafana 같은 대시보드에 아래를 함께 띄워둡니다.

  • 부하 도구가 보낸 rps와 받은 응답 코드 분포(2xx·429·5xx)
  • 원본 서버의 CPU·메모리·활성 커넥션·워커 사용률
  • 방어 계층 로그 — rate limit 차단 수, WAF 룰 매치, SYN cookie 발동 카운터

판정 기준은 “서버가 안 죽었다”가 아니라 이렇게 잡아요. 부하가 오르는 동안 방어가 계획한 지점에서 켜지고, 그 덕에 원본의 핵심 지표(p95·에러율)가 안전 범위 안에 머물렀는가. 방어가 켜졌는데도 원본이 휘청였다면 그 계층 방어가 부족한 거고, 방어가 아예 안 켜졌다면 설정이 잘못 걸린 거예요.



7. 결과를 방어로 되먹이기

테스트의 진짜 값어치는 숫자 자체가 아니라 그 뒤의 튜닝에 있어요. 관찰한 결과를 방어 설정으로 되돌려 넣고, 다시 같은 테스트를 돌려 개선을 확인하는 루프를 만듭니다.

  • L7 — rate limit rate·burst 조정, WAF 룰 보강, 오토스케일 임계·워커 수 재설정. 앞에서 쓴 k6 스크립트를 그대로 다시 돌려 429 발동 지점과 p95가 나아졌는지 비교.
  • L4 — SYN cookie 확인, backlog·conntrack 상한 튜닝. 랩에서 재검증.
  • 정례화 — 한 번 하고 끝내지 말고, 배포·인프라 변경 뒤 정기적으로 돌려요. 팀이 함께 모여 시나리오를 돌려보는 게임데이로 만들면 대응 절차까지 같이 담금질됩니다.

이렇게 “가설 → 부하 → 관측 → 튜닝 → 재검증”이 한 바퀴 돌면, 5편에서 세운 방어가 종이 위 설정이 아니라 실제로 검증된 방벽이 됩니다.



8. 실행 전 체크리스트

마지막으로 테스트를 돌리기 직전에 훑는 체크리스트예요.

  • 대상이 내가 소유·관리하는 자산인가
  • 클라우드/업스트림 사업자 승인(또는 폐쇄 랩 격리)을 확보했나
  • 프로덕션이 아니라 스테이징/랩을 대상으로 하나
  • 측정 지표와 통과 기준을 미리 정했나
  • 중단(kill switch) 조건과 담당자를 정했나
  • 부터·스트레서·봇넷을 쓰지 않고 정식 부하 도구만 쓰나
  • 테스트 후 원상복구·회고 계획이 있나

하나라도 비어 있으면 시작하지 않는 게 맞아요.



마치며

DDoS 6부작은 여기서 마무리할게요…..
개념과 공격 지형도에서 출발해 볼류메트릭·프로토콜·L7 공격을 뜯어봤고, 방어를 계층별로 쌓은 뒤, 마지막으로 그 방어를 내 손으로 합법적으로 검증하는 데까지 왔습니다. 공격을 이해하는 이유는 결국 잘 막기 위해서고, 잘 막았는지는 두들겨봐야 압니다. 다만 그 두들김은 늘 내가 책임질 수 있는 자산 안에서, 남에게 피해가 가지 않는 방식으로 해야 해요. 다음엔 또 다른 보안 주제로 찾아올게요.



← 이전 글: (5/6) DDoS 방어와 완화 — 탐지부터 스크러빙까지


참고: k6 — Load testing documentation · AWS — DDoS Simulation Testing policy · OWASP — Denial of Service Cheat Sheet