공개 인터넷에 노출된 WebSocket 서버는 매력적인 공격 대상입니다. 이 프로토콜에는 내장된 메시지 크기 제한이 없어서, 단 하나의 연결만으로 조작된 프레임 하나로 전체 프로세스를 무너뜨리려 시도할 수 있으며, 업그레이드 핸드셰이크는 허술한 검증을 탐색하기 좋은 지점입니다. 최신 sgcWebSockets는 이러한 문을 기본적으로 닫는 일련의 보호 기능을 제공하므로, 적대적인 트래픽 속에서도 서버가 계속 동작합니다.
아래의 모든 내용은 세 가지 WebSocket 서버 모두에 적용됩니다. TsgcWebSocketServer, TsgcWebSocketHTTPServer 그리고 http.sys 기반의 TsgcWebSocketServer_HTTPAPI입니다. .NET 에디션은 동일한 네이티브 라이브러리 위에서 동작하므로 동일한 보호 기능을 그대로 물려받습니다.
세 가지 메모리 공격을 막는 하나의 제한
핵심 추가 사항은 인바운드 메시지의 최대 크기를 제한하는 단일 속성 MaxMessageSize입니다. 기본값은 64 MB로, 거의 모든 애플리케이션에 넉넉하며, 값을 올리거나 내릴 수 있고, 0으로 설정하면 제한을 비활성화할 수 있습니다.
이 하나의 속성은 모두 동일한 결과, 즉 서버의 RAM 고갈로 끝나는 세 가지 서로 다른 메모리 고갈 기법을 방어합니다.
- 과도한 크기의 프레임. 클라이언트가 거대한 페이로드 길이를 선언합니다. 이제 서버는 본문을 읽기 시작하기 전에 해당 프레임을 거부합니다.
- 끝없는 단편화. 클라이언트가 단편화된 메시지를 열어 놓고 끝내지 않은 채, 연속 프레임을 영원히 스트리밍합니다. 이제 서버는 모든 단편에 걸쳐 재조립된 전체 크기를 제한합니다.
- 압축 폭탄. 기가바이트로 부풀어 오르는 작은 per-message-deflate 프레임입니다. 이제 서버는 부풀려진 출력이 제한에 도달하는 순간 압축 해제를 중단합니다.
어느 경우든 연결은 WebSocket close code 1009 (Message Too Big)로 깔끔하게 닫히며, 서버의 메모리는 결코 한계를 넘지 않습니다.
oServer := TsgcWebSocketServer.Create(nil);
oServer.Port := 80;
// accept messages up to 16 MB, reject anything larger with close 1009
oServer.MaxMessageSize := 16 * 1024 * 1024;
oServer.Active := True;
안전한 프레임 길이 파싱
WebSocket 프레임은 64-bit 길이 필드를 담을 수 있습니다. RFC 6455에 따르면 이 필드의 최상위 비트는 반드시 0이어야 합니다. 이제 sgcWebSockets는 64-bit 길이의 최상위 비트가 설정된 프레임을 신뢰하는 대신 거부하므로, 위의 크기 제한을 정수 오버플로로 우회할 수 없습니다. 이 검사는 항상 켜져 있으며 MaxMessageSize에 의존하지 않습니다.
더 엄격한 핸드셰이크
SecurityOptions 아래의 두 가지 새 옵션이 WebSocket 업그레이드 자체를 강화하며, 둘 다 기본적으로 활성화되어 있습니다.
EnforceWebSocketVersion은 13이 아닌 버전을 요청하는 핸드셰이크에 대해 업그레이드를 완료하는 대신 426 Upgrade Required와 Sec-WebSocket-Version: 13 헤더로 응답합니다. ValidateWebSocketKey는 Sec-WebSocket-Key가 없거나 유효한 16바이트 base64 논스가 아닌 핸드셰이크를 400 Bad Request로 거부합니다. 두 검사 모두 RFC 6455 경로에만 적용되므로, 레거시 사양을 사용하는 구형 클라이언트는 영향을 받지 않습니다.
oServer := TsgcWebSocketServer.Create(nil);
oServer.SecurityOptions.EnforceWebSocketVersion := True; // 426 on a wrong version
oServer.SecurityOptions.ValidateWebSocketKey := True; // 400 on a malformed key
// lock the server to your own site while you are at it
oServer.SecurityOptions.OriginsAllowed := 'https://app.example.com';
oServer.Active := True;
방화벽과의 조화
이러한 보호 기능은 프로토콜 계층, 즉 프레임 파서 내부에 위치하며, 그곳이 바로 메모리 공격이 발생하는 지점입니다. Firewall과 RateLimiter 컴포넌트는 메시지가 디코딩된 후에야 그것을 보기 때문에 도달할 수 없는 부분입니다. 두 계층은 서로를 보완합니다. IP 필터링, 연결 및 메시지 속도 제한, 오리진 정책에는 방화벽과 속도 제한기를 계속 사용하고, 새로운 내장 제한이 파서 자체를 보호하도록 하면 됩니다. 공개 서버의 경우 MaxMessageSize를 실제 최대값으로 설정하고, OriginsAllowed를 프런트엔드로 잠그며, MaxConnections에 상한을 두는 것을 권장합니다.
업그레이드
새로운 보호 기능은 안전한 기본값과 함께 업데이트하는 즉시 활성화되므로, 대부분의 서버는 단 한 줄의 코드 변경 없이도 메모리 및 핸드셰이크 강화를 얻습니다. 애플리케이션이 정당하게 64 MB보다 큰 메시지를 주고받는다면, 그에 맞게 MaxMessageSize를 올리십시오. 기존 클라이언트 코드는 영향을 받지 않습니다.
sgcWebSockets 다운로드 페이지에서 업데이트하거나, GetIt 또는 등록된 계정을 통해 받으십시오.
질문, 피드백 또는 마이그레이션 도움이 필요하신가요? 문의하기, 코드를 작성한 사람들로부터 답변을 받으실 수 있습니다.
