← 문제로

25. SYN 플러드와 연결 고갈 방어: SYN 쿠키·백로그·accept 큐 vs SYN 큐

난이도 중
내 답안
모범답안

해설 — SYN 플러드와 연결 고갈 방어

난이도: 중상

1) 3-way 핸드셰이크와 두 큐

  • 클라이언트 SYN → 서버 SYN-ACK → 클라이언트 ACK 로 연결이 수립된다.
  • 커널은 리슨 소켓마다 두 개의 큐를 둔다.
    • SYN 큐(incomplete/half-open queue): 서버가 SYN 을 받아 SYN-ACK 를 보낸 뒤 최종 ACK 를 기다리는 연결. 이 상태가 SYN_RECV. 크기 상한은 대략 tcp_max_syn_backlog.
    • Accept 큐(completed queue): 핸드셰이크가 끝난(ESTABLISHED) 연결이 대기하는 곳. 상한은 listen(backlog) 와 net.core.somaxconn 의 min.
  • 애플리케이션 accept() 는 accept 큐에서만 꺼낸다. SYN 큐에 있는 연결은 아직 애플리케이션에 보이지 않는다. 최종 ACK 가 오면 커널이 연결을 SYN 큐→accept 큐로 옮긴다.

2) SYN 플러드의 원리

  • 공격자가 SYN 만 대량으로, 흔히 위조 출발지 IP 로 보낸다. 서버는 각 SYN 에 대해 SYN 큐 엔트리를 잡고 SYN-ACK 를 보낸 뒤 최종 ACK 를 기다린다.
  • 최종 ACK 는 오지 않는다(위조 IP 라 SYN-ACK 가 엉뚱한 곳으로 가거나 버려짐). 엔트리는 재전송 타임아웃 동안(수십 초) 큐를 점유한다.
  • 고갈되는 자원은 SYN 큐(그리고 SYN-ACK 재전송에 쓰는 약간의 대역폭). SYN 큐가 가득 차면 커널은 새 SYN 을 드롭하므로 정상 사용자의 SYN 도 거부된다. CPU·애플리케이션은 멀쩡한데 접속만 안 되는 증상이 이래서 나온다. 이는 상태 보유형 자원(half-open 엔트리)을 소량의 SYN 으로 소진시키는 비대칭 공격이다.

3) SYN 쿠키

  • 핵심 아이디어: 서버가 half-open 상태를 저장하지 않는다. SYN 큐가 가득 차면, 엔트리를 만드는 대신 필요한 정보를 SYN-ACK 의 초기 시퀀스 번호(ISN) 에 암호학적으로 인코딩해 클라이언트에게 "떠넘긴다".
  • ISN(쿠키)에는 대략 (a) 시간에 따라 증가하는 카운터, (b) 연결 4-튜플(출발지/목적지 IP·포트)과 서버 비밀키의 해시, (c) MSS 등 최소 정보가 담긴다.
  • 최종 ACK 가 오면 그 ACK 번호 = 쿠키+1 이므로, 서버는 저장된 상태 없이 ACK 안의 값만 검증(비밀키로 재계산·비교)해 정상 핸드셰이크인지 확인하고 그 자리에서 연결을 수립한다. 위조 SYN 에는 최종 ACK 가 없으니 아무 자원도 남지 않는다.
  • 한계: ISN 비트 예산이 작아 TCP 옵션 일부(윈도우 스케일, SACK, 타임스탬프 중 저장 못 하는 것)가 유실될 수 있어 처리량이 떨어질 수 있다. 그래서 보통 SYN 큐가 임계에 도달했을 때만 쿠키로 전환한다(tcp_syncookies=1 의 기본 동작).

4) 운영 방어

  • 커널 튜닝: net.ipv4.tcp_syncookies=1(임계 초과 시 쿠키), tcp_max_syn_backlog 상향, net.core.somaxconn 과 listen() backlog 상향(accept 큐), tcp_synack_retries 하향(죽은 half-open 을 빨리 정리).
  • 연결 rate limiting / SYN 프록시: 방화벽(iptables synproxy), L4 로드밸런서·DDoS 방어 어플라이언스가 핸드셰이크를 대신 완성한 뒤 정상 연결만 백엔드로 넘긴다.
  • 앞단 완충: 게임 로그인은 보통 로드밸런서·게이트웨이 뒤에 둔다. SYN 플러드는 이 앞단(또는 클라우드 DDoS 보호)에서 흡수하고, 백엔드는 인증·세션에 집중한다.
  • 게임 로그인 서버 조합(권장): 클라우드/앞단 DDoS 보호 + LB SYN 프록시로 1차 방어, 백엔드는 tcp_syncookies=1·넉넉한 backlog·짧은 synack_retries 로 2차 방어, 그리고 IP 당 연결 rate limit. 로그인은 트래픽 스파이크가 잦으므로 정상 폭주와 공격을 구분하는 임계·화이트리스트도 함께 둔다.

5) 애플리케이션 레벨과의 구분

  • 커널 계층(이 문제): half-open(SYN_RECV) 고갈. 애플리케이션이 관여하기 전, 커널 TCP 스택에서 벌어진다. 방어는 커널 튜닝·SYN 쿠키·앞단 프록시.
  • 애플리케이션 계층(slowloris 류): 연결은 정상적으로 accept() 되지만, 클라이언트가 인증/핸드셰이크 데이터를 아주 느리게 보내거나 안 보내 워커·세션 슬롯·미인증 연결 자원을 오래 점유한다. accept 큐가 아니라 애플리케이션의 미인증 연결 상한·핸드셰이크 타임아웃이 방어선이다(미인증 연결 수 상한, 핸드셰이크 데드라인, idle 타임아웃).
  • 두 계층은 자원도 방어 위치도 다르다. SYN 쿠키는 slowloris 를 못 막고, 애플리케이션 핸드셰이크 타임아웃은 SYN 플러드를 못 막는다. 양쪽 다 둬야 한다.

핵심 요약

SYN 플러드는 소량의 위조 SYN 으로 커널의 half-open 상태를 소진시키는 비대칭 공격이고, SYN 쿠키는 상태를 시퀀스 번호에 인코딩해 서버가 기억하지 않게 함으로써 이를 무력화한다. 다만 옵션 유실 한계가 있어 임계 초과 시에만 켠다. 커널 계층(SYN 큐)과 애플리케이션 계층(미인증 연결) 방어는 별개이며 함께 필요하다.