How it works: HTTP/2의 새로운 'Rapid Reset' DDoS 공격
Juho Snellman
Staff Software Engineer
Daniele Iamartino
Staff Site Reliability Engineer
Hear monthly from our Cloud CISO in your inbox
Get the latest on security from Cloud CISO Phil Venables.
Subscribe*본 아티클의 원문은 2023년 10월 11일 Google Cloud 블로그(영문)에 게재되었습니다.
Google의 다양한 서비스 및 Cloud 고객이 8월에 최고조에 달한 새로운 HTTP/2 기반 DDoS 공격의 표적이 되었습니다. 이러한 공격은 보고된 바 있는 모든 이전 Layer 7 공격보다 규모가 훨씬 컸으며, 가장 큰 공격은 초당 3억 9,800만 건의 요청을 초과했습니다.
이러한 공격은 Google의 글로벌 로드 밸런싱 인프라에 의해 네트워크 에지에서 대부분 차단되었으며, 서비스 중단으로 이어지지는 않았습니다. 영향은 미미했지만 Google의 DDoS 대응팀은 이러한 공격을 검토하고 유사한 공격을 추가로 방어하기 위해 보호 기능을 추가했습니다. Google의 내부 대응 외에도 업계 파트너와 함께 조정된 공개 프로세스를 이끌어 냄으로써 에코시스템 전반에 걸친 새로운 HTTP/2 벡터를 해결했습니다.
지난 몇 년 동안 레이어 7 공격의 주된 방법론, 이러한 새로운 공격에서 규모가 훨씬 커지도록 변경된 내용 및 이러한 공격 유형에 효과적이라고 생각되는 방어 전략에 대해 설명드리겠습니다. 이 문서는 HTTP 요청이 다른 서비스로 요청을 전달하는 리버스 프록시 아키텍처의 관점에서 작성되었습니다. 동일한 개념이 애플리케이션 서버에 통합된 HTTP 서버에도 적용되지만 약간 다른 고려 사항이 있으며 잠재적으로 다른 방어 전략으로 이어질 수 있습니다.
HTTP/2에 대한 기초 DDoS
2021년 후반부터 구글의 자사 서비스 및 Cloud Armor로 보호되는 Google Cloud 프로젝트에서 관찰된 대부분의 레이어 7 DDoS 공격은 공격 횟수와 최대 요청 속도 측면에서 모두 HTTP/2를 기반으로 진행되었습니다.
HTTP/2의 주요 설계 목표는 효율성이었으며, 안타깝게도 합법적인 클라이언트에게 HTTP/2를 더 효율적으로 만드는 기능이 더욱 강력한 DDoS 공격을 만드는 데 사용될 수도 있습니다.
스트림 multiplexing
HTTP/2는 양방향 추상화인 "스트림"을 사용하여 엔드포인트 간에 다양한 메시지 또는 "프레임"을 전송합니다. "스트림 multiplexing"은 각 TCP 연결의 사용률을 높일 수 있는 HTTP/2의 핵심 기능입니다. 스트림은 하나의 레이어 4 연결만 사용하면서 연결의 양쪽에서 추적할 수 있는 방식으로 다중화됩니다. 스트림 multiplexing을 통해 클라이언트는 다수의 개별 연결을 생성하지 않고도 여러 개의 진행중인 요청을 보낼 수 있습니다.
레이어 7 DDoS 공격을 실행할 때의 주요 제약 조건 중 하나는 동시 전송 연결의 수입니다. 각 연결에는 소켓 레코드 및 버퍼용 운영 체제 메모리, TLS 핸드셰이크용 CPU 시간뿐만 아니라 각 연결에 고유한 4중 튜플(연결 양측의 IP 주소 및 포트 쌍)이 필요하므로 두 IP 주소 간의 동시 연결 수를 제한합니다.
HTTP/1.1에서는 각 요청이 직렬로 처리됩니다. 서버는 요청을 읽고 처리하고 응답을 작성한 다음 다음 요청을 읽고 처리합니다. 실제로 이는 단일 연결을 통해 전송될 수 있는 요청 속도가 왕복당 하나의 요청임을 의미합니다. 여기서 왕복은 네트워크 지연 시간, 프록시 처리 시간 및 백엔드 요청 처리 시간을 포함합니다. HTTP/1.1 파이프라이닝은 일부 클라이언트 및 서버에서 사용 가능하지만 합법적인 클라이언트 사이에서는 만연하지 않습니다.
HTTP/2를 사용하면 클라이언트는 단일 TCP 연결에서 여러 개의 동시 스트림을 열 수 있으며 각 스트림은 하나의 HTTP 요청에 해당합니다. 동시 열려 있는 스트림의 최대 수는 이론적으로 서버에서 제어할 수 있지만 실제로는 클라이언트가 요청당 100개의 스트림을 열고 서버가 이러한 요청을 병렬로 처리할 수 있습니다. 서버 제한은 일방적으로 조정할 수 없다는 점에 유의하는 것이 중요합니다.
예를 들어 클라이언트는 단일 왕복 시간에 100개의 스트림을 열고 각 스트림에 대해 요청을 보낼 수 있습니다. 프록시는 각 스트림을 직렬로 읽고 처리하지만 백엔드 서버에 대한 요청은 다시 병렬화될 수 있습니다. 그런 다음 클라이언트는 이전 스트림에 대한 응답을 받으면 새 스트림을 열 수 있습니다. 이를 통해 HTTP/1.1 요청과 유사한 왕복 시간 상수를 가진 단일 연결에 대해 왕복당 100개의 요청의 효과적인 처리량을 제공합니다. 이는 일반적으로 각 연결의 사용률이 거의 100배 높아짐으로 이어집니다.
HTTP/2 Rapid Reset 공격
HTTP/2 프로토콜은 클라이언트가 RST_STREAM 프레임을 보내어 이전 스트림을 취소하도록 서버에 지시할 수 있도록 합니다. 프로토콜은 클라이언트와 서버가 취소를 어떤 식으로든 조정할 필요가 없으며 클라이언트가 일방적으로 취소할 수 있습니다. 또한 클라이언트는 서버가 해당 TCP 연결의 다른 데이터를 처리하기 전에 RST_STREAM 프레임을 수신하면 취소가 즉시 적용될 것으로 가정할 수 있습니다.
이 공격은 엔드포인트가 요청 프레임을 보낸 직후에 RST_STREAM 프레임을 보낼 수 있는 기능에 의존하기 때문에 Rapid Reset이라고 불립니다. 이를 통해 다른 엔드포인트가 작업을 시작한 다음 요청을 즉시 재설정합니다. 요청은 취소되지만 HTTP/2 연결은 열려 있습니다.
HTTP/1.1 and HTTP/2 request and response pattern
HTTP/2 Rapid Reset 공격은 이 기능을 기반으로 구축되어 간단합니다. 클라이언트는 표준 HTTP/2 공격에서와 같이 한 번에 다량의 스트림을 열지만 서버 또는 프록시에서 각 요청 스트림에 대한 응답을 기다리지 않고 클라이언트는 각 요청을 즉시 취소합니다.
스트림을 즉시 재설정하는 기능을 사용하면 각 연결이 진행 중인 요청 수를 무제한으로 유지할 수 있습니다. 공격자는 요청을 명시적으로 취소함으로써 동시 열려 있는 스트림의 수에 대한 제한을 초과하지 않습니다. 진행중인 요청 수는 더 이상 왕복 시간(RTT)에 의존하지 않고 사용 가능한 네트워크 대역폭에만 의존합니다.
일반적인 HTTP/2 서버 구현에서 서버는 취소된 요청에 대해 여전히 새 스트림 데이터 구조 할당, 쿼리 구문 분석 및 헤더 압축 해제, URL을 리소스에 매핑하는 등 상당한 양의 작업을 수행해야 합니다. 역방향 프록시 구현의 경우 RST_STREAM 프레임이 처리되기 전에 요청이 백엔드 서버로 프록시될 수 있습니다. 반면에 클라이언트는 요청을 보내는 데 거의 비용을 지불하지 않았습니다. 이는 서버와 클라이언트 간에 악용 가능한 비용 비대칭을 생성합니다.
공격자가 얻는 또 다른 이점은 요청이 생성된 직후 명시적으로 취소된다는 것은 역방향 프록시 서버가 어떤 요청에도 응답을 보내지 않음을 의미한다는 것입니다. 응답이 작성되기 전에 요청을 취소하면 다운링크(서버/프록시에서 공격자로) 대역폭이 줄어듭니다.
HTTP/2 Rapid Reset 공격 변형
최초의 DDoS 공격 이후 몇 주 동안 일부 Rapid Reset 공격 변형이 발견되었습니다. 이러한 변형은 일반적으로 초기 버전만큼 효율적이지는 않지만 표준 HTTP/2 DDoS 공격보다 여전히 효율적일 수 있습니다.
첫 번째 변형은 스트림을 즉시 취소하지 않고 한 번에 스트림 배치를 열고 잠시 기다린 다음 해당 스트림을 취소한 다음 즉시 새 스트림의 또 다른 대량 배치를 엽니다. 이 공격은 연결당 초당 최대 100개의 RST_STREAM 프레임만 허용하는 등 인바운드 RST_STREAM 프레임의 속도에만 기반한 방어를 우회할 수 있습니다.
이러한 공격은 연결 활용을 극대화하지 않음으로써 취소 공격의 주요 이점을 잃지만 표준 HTTP/2 DDoS 공격보다 여전히 일부 구현 효율성을 가지고 있습니다. 그러나 이 변형은 스트림 취소 속도 제한에 기반한 모든 방어가 효과적이기 위해 상당히 엄격한 제한을 설정해야 한다는 것을 의미합니다.
두 번째 변형은 스트림 취소를 완전히 없애고 대신 서버가 광고한 것보다 더 많은 동시 스트림을 열려고 시도합니다. 이 접근 방식의 이점은 표준 HTTP/2 DDoS 공격에 비해 클라이언트가 요청 파이프라인을 항상 가득 채울 수 있으며 클라이언트-프록시 RTT를 병목 현상으로 제거할 수 있다는 것입니다. 또한 HTTP/2 서버가 즉시 응답하는 리소스에 대한 요청인 경우 프록시-서버 RTT를 병목 현상으로 제거할 수 있습니다.
현재 HTTP/2 RFC인 RFC 9113은 스트림을 너무 많이 열려고 시도하면 한도를 초과한 스트림만 무효화하고 전체 연결은 무효화하지 않도록 제안합니다. 대부분의 HTTP/2 서버는 이러한 스트림을 처리하지 않을 것이며 이전 스트림에 응답한 후 거의 즉시 새로운 스트림을 수락하고 처리함으로써 취소하지 않는 공격 변형을 가능하게 합니다.
방어를 위한 다각적인 접근
우리는 단순히 개별 요청을 차단하는 것이 이러한 유형의 공격에 실행 가능한 대응책이라고 생각하지 않습니다. 대신 남용이 감지되면 전체 TCP 연결을 닫아야 합니다. HTTP/2는 GOAWAY 프레임 유형을 사용하여 연결을 닫기 위한 기본 기능을제공합니다. RFC는 새로운 스트림을 여는 데 제한을 설정하지 않는 정보 GOAWAY를 먼저 보내고 한 라운드 트립 후 추가 스트림을 여는 것을 금지하는 또 다른 GOAWAY를 보내는 연결을 닫는 프로세스를 정의합니다.
그러나 이 GOAWAY 프로세스는 일반적으로 악의적인 클라이언트에 강력한 방식으로 구현되지 않습니다. 이러한 형태의 방어는 연결을 Rapid Reset 공격에 너무 오랫동안 취약하게 남겨두며 인바운드 요청을 중지하지 않으므로 방어를 구축하는 데 사용되어서는 안 됩니다. 대신 GOAWAY는 스트림 생성을 즉시 제한하도록 설정되어야 합니다.
이는 어떤 연결이 악용적인지 결정하는 문제를 남깁니다. 클라이언트가 요청을 취소하는 것은 본질적으로 남용적인 것은 아니며, 이 기능은 HTTP/2 프로토콜에 존재하여 요청 처리를 보다 효율적으로 관리할 수 있도록 합니다. 일반적인 상황은 사용자가 페이지에서 멀리 이동하여 브라우저가 더 이상 요청한 리소스가 필요하지 않거나 클라이언트 측 타임아웃을 사용하는 긴 폴링(long polling) 접근 방식을 사용하는 응용 프로그램입니다.
이 공격 벡터에 대한 방어는 여러 형태를 취할 수 있지만 대부분 연결 통계를 추적하고 다양한 신호와 비즈니스 로직을 사용하여 각 연결의 유용성을 결정하는 데 중심을 둡니다. 예를 들어 연결에 100개 이상의 요청이 있고 지정된 요청의 50% 이상이 취소된 경우 방어 응답 후보가 될 수 있습니다. 응답의 크기와 유형은 각 플랫폼에 대한 위험에 따라 다르지만 응답은 앞에서 설명한 강제 GOAWAY 프레임에서 TCP 연결을 즉시 닫는 것까지 다양합니다.
취소하지 않는 변형의 공격을 방어하기 위해 HTTP/2 서버는 동시 스트림 제한을 초과하는 연결을 닫아야 한다고 권장합니다. 이는 즉시 또는 소수의 반복 위반 후에 수행될 수 있습니다.
다른 프로토콜에 대한 적용성
우리는 프로토콜 차이로 인해 이러한 공격 방법이 HTTP/3(QUIC)으로 직접 변환될 수 있다고 생각하지 않으며 Google은 현재 대규모 DDoS 공격 벡터로 사용되는 HTTP/3를 보지 못하고 있습니다. 그럼에도 불구하고 우리의 권장 사항은 HTTP/3 서버 구현이 위에서 논의한 HTTP/2 방어와 유사하게 단일 전송 연결에서 수행되는 작업량을 제한하는 메커니즘을 사전에 구현하는 것입니다.
업계 협력
DDoS 대응팀의 조사 초기 및 업계 파트너와의 협력으로 이 새로운 공격 유형이 서비스에 HTTP/2 프로토콜을 제공하는 모든 엔티티에 광범위한 영향을 미칠 수 있다는 것이 분명해졌습니다. Google은 과거에 다른 여러 노력에 사용되었던 기존 조정된 취약점 공개 그룹을 활용하여 조정된 취약점 공개 프로세스를 주도하는 데 도움을 주었습니다.
공개 과정에서 팀은 인프라 회사 및 서버 소프트웨어 공급자를 포함한 대규모 HTTP/2 구현자에게 통보하는 데 중점을 두었습니다. 이러한 사전 통보의 목표는 조정된 릴리스를 위한 방어책을 개발하고 준비하는 것이었습니다. 과거에 이 접근 방식은 서비스 제공자를 위한 광범위한 보호 기능을 활성화하거나 많은 패키지 및 솔루션에 대한 소프트웨어 업데이트를 통해 사용할 수 있도록 했습니다.
조정된 공개 과정에서 다양한 HTTP/2 구현에 대한 수정 사항을 추적하기 위해 CVE-2023-44487을 예약했습니다.
다음 단계
이 게시물에서 논의된 새로운 공격은 모든 규모의 서비스에 큰 영향을 미칠 수 있습니다. HTTP/2 서비스가 있는 모든 제공자는 이 문제에 대한 노출을 평가해야 합니다. 일반적인 웹 서버 및 프로그래밍 언어에 대한 소프트웨어 패치 및 업데이트는 현재 또는 가까운 미래에 적용할 수 있습니다. 가능한 한 빨리 이러한 수정 사항을 적용하는 것이 좋습니다.
고객의 경우 소프트웨어 패치를 적용하고 Google 및 기존 Google Cloud Application Load Balancing 사용자를 보호하고 있는 Application Load Balancer 및 Google Cloud Armor를 활성화하는 것이 좋습니다.