CONNECT
사용법
CONNECT 요청은 HTTP 프록시에게 지정된 호스트와 포트로 TCP 연결을 수립한 뒤 원시 바이트를 양방향으로 중계하라고 지시합니다. 요청 대상은 경로가 아니라 authority-form(host:port)을 사용합니다.
CONNECT의 주된 용도는 HTTPS 터널링입니다. 프록시 뒤의 클라이언트가 HTTPS 사이트에 도달해야 할 때 그 과정은 다음과 같이 진행됩니다.
터널이 수립된 뒤 프록시는 원시 바이트를 전달하기만 하므로, 프록시는 암호화된 트래픽의 내용을 들여다볼 수 없습니다. 이것이 기업과 네트워크 프록시가 외부 서버로의 HTTPS 접근을 허용하는 데 사용하는 메커니즘입니다.
프록시가 인증을 요구하면 클라이언트는 CONNECT 요청에 Proxy-Authorization 헤더를 포함합니다. 자격 증명이 없거나 유효하지 않으면 407 Proxy Authentication Required 응답이 돌아옵니다.
WHATWG Fetch Standard는 CONNECT를 금지된 메서드로 분류하여, JavaScript가 브라우저 API를 통해 CONNECT 요청을 보내지 못하게 합니다.
HTTP/2와 HTTP/3에서 CONNECT는 TCP 연결 전체가 아니라 하나의 스트림 위에 터널을 만듭니다. HTTP/2 명세는 개별 스트림을 위한 기본 CONNECT를 정의합니다. 확장 CONNECT 프로토콜 업그레이드는 :protocol 유사 헤더를 추가해 다중화된 연결 위에서 WebSocket을 비롯한 프로토콜을 가능하게 하며, HTTP/2와 HTTP/3 모두에서 지원됩니다. 기본 스트림 터널과 확장 CONNECT는 서로를 보완합니다. 하나는 원시 터널링을 제공하고, 다른 하나는 다중화된 연결 위에서 TCP가 아닌 프로토콜을 쓸 수 있게 해 줍니다.
프록시를 통한 HTTPS 터널
클라이언트가 프록시를 거쳐 443 포트의 example.com으로 TLS 터널을 요청합니다. 프록시는 Proxy-Authorization 헤더로 클라이언트를 인증하고, TCP 연결을 연 뒤 200 OK로 확인해 줍니다.
요청
CONNECT example.com:443 HTTP/1.1
Host: example.com:443
Proxy-Authorization: Basic dXNlcjpwYXNz
응답
HTTP/1.1 200 Connection Established
200 응답을 받은 뒤 클라이언트는 열린 터널을 통해 example.com과 TLS 핸드셰이크를 시작합니다. 프록시는 이후의 모든 데이터를 내용을 들여다보거나 변경하지 않고 전달합니다.
인증이 없는 터널
프록시가 자격 증명을 요구하지 않을 때는 요청에 대상 호스트와 포트만 담깁니다.
요청
CONNECT example.com:443 HTTP/1.1
Host: example.com:443
응답
HTTP/1.1 200 Connection Established
CORS
CONNECT는 CORS 금지 메서드입니다. 브라우저는 fetch()나 XMLHttpRequest 같은 브라우저 API로 CONNECT를 보내려는 모든 시도를 차단합니다.
HTTP 터널링
HTTP 터널링은 HTTP가 아닌 트래픽을 HTTP 메시지 안에 캡슐화하여 HTTPS, SSH, FTP 같은 프로토콜이 HTTP 프록시를 통과할 수 있게 합니다. CONNECT는 이러한 터널을 수립하는 주된 메커니즘입니다. 프록시는 대상 호스트와 포트로 원시 TCP 연결을 연 뒤, 내용을 들여다보지 않고 바이트를 양방향으로 중계합니다.
클라이언트와 서버 사이에 프록시가 없다면 CONNECT는 불필요합니다. 클라이언트는 대상 프로토콜로 서버에 직접 연결합니다.
보안
임의의 포트로의 CONNECT를 받아들이는 열린 프록시는 악용을 가능하게 합니다. SMTP(25번 포트)를 통한 스팸 중계, 내부 네트워크 포트 스캔, 네트워크 접근 통제 우회가 그런 예입니다. 운영 환경의 프록시는 CONNECT를 443 포트(HTTPS)나 승인된 목적지 허용 목록으로 제한합니다.
OWASP는 제한 없는 CONNECT를 보안 위험으로 지목합니다. WHATWG Fetch Standard는 CONNECT를 금지된 메서드로 분류하여, 브라우저가 fetch()나 XMLHttpRequest로 CONNECT 요청을 직접 보내지 못하게 합니다.