본문으로 건너뛰기
피드

30살 된 FastCGI가 아직도 리버스 프록시 백엔드 프로토콜로 더 낫다는 주장

backend 약 8분

HTTP를 리버스 프록시와 백엔드 사이 프로토콜로 쓰는 관행이 desync 공격과 신뢰 헤더 문제를 계속 만든다는 글이다. 저자는 FastCGI가 1996년 나온 오래된 프로토콜이지만 명시적 프레이밍과 신뢰 정보 분리 덕분에 이 구간에서는 HTTP보다 안전한 선택일 수 있다고 주장한다.

  • 1

    HTTP/1.1은 메시지 경계가 애매해 리버스 프록시와 백엔드가 서로 다르게 파싱할 수 있고, 이게 request smuggling의 뿌리다.

  • 2

    FastCGI는 요청 경계를 명확히 나누고, 클라이언트 헤더와 프록시가 넣은 신뢰 정보를 구조적으로 분리한다.

  • 3

    nginx는 첫 릴리스부터 FastCGI 백엔드를 지원했지만, HTTP/2 백엔드 지원은 2025년 말에야 들어갔다.

  • 4

    FastCGI는 WebSocket 미지원, 부족한 도구, 덜 최적화된 성능 같은 현실적인 단점도 있다.

  • 리버스 프록시와 백엔드 사이에 HTTP를 쓰는 게 너무 당연해졌지만, 이 글은 그 기본값이 꽤 위험하다고 찌름

    • 최근 Discord 미디어 프록시에서도 desync 취약점이 공개됐고, 이 취약점은 비공개 첨부파일을 엿볼 수 있는 수준이었다고 함
    • 저자는 이런 문제가 특이한 사고가 아니라 HTTP 리버스 프록시 구조에서 반복적으로 나오는 패턴이라고 봄
  • 핵심 원인은 HTTP/1.1의 메시지 경계가 애매하다는 점임

    • HTTP/1.1은 겉으로는 텍스트라 단순해 보이지만, 같은 메시지도 여러 방식으로 표현할 수 있고 파서마다 해석이 달라질 수 있음
    • 메시지가 어디서 끝나고 다음 메시지가 어디서 시작되는지 프록시와 백엔드가 다르게 보면 request smuggling, 즉 HTTP desync 공격이 가능해짐
    • 보안 연구자 James Kettle은 이런 류의 파서 차이를 계속 찾아냈고, 결국 “HTTP/1.1 must die”라고까지 말했다고 함

⚠️주의

> HTTP desync는 단순 파서 버그 하나 고치는 문제로 끝나지 않음. 프로토콜 자체가 애매한 경계를 허용하면 구현체 차이가 계속 공격면이 됨.

  • FastCGI는 1996년에 나온 30년 된 프로토콜이지만, 이 문제 하나만큼은 처음부터 더 깔끔하게 피하고 있었음

    • FastCGI는 프로세스 실행 모델이 아니라 wire protocol이라, 백엔드 데몬에 TCP나 유닉스 소켓으로 요청을 보내는 식으로 쓸 수 있음
    • Go에서는 net/http/fcgi를 import하고 http.Servefcgi.Serve로 바꾸면 기존 http.ResponseWriter, http.Request 핸들러를 그대로 쓸 수 있다고 함
    • Apache, Caddy, nginx, HAProxy 모두 FastCGI 백엔드를 지원하고, 설정도 HTTP 프록시 설정과 크게 다르지 않음
  • HTTP/2는 메시지 프레이밍을 명확히 해서 desync 문제를 줄이지만, 배포 현실은 그렇게 단순하지 않음

    • 프록시와 백엔드 사이에서 HTTP/2를 일관되게 써야 효과가 있는데, nginx는 첫 릴리스부터 FastCGI 백엔드를 지원한 반면 HTTP/2 백엔드 지원은 2025년 말에야 들어갔다고 함
    • Apache의 HTTP/2 백엔드 지원도 아직 “experimental” 상태라고 언급됨
    • 반대로 FastCGI는 1996년부터 명확한 요청 경계를 가진 더 단순한 프로토콜이었다는 게 저자의 포인트임
  • 두 번째 문제는 프록시가 넣은 신뢰 정보를 HTTP 헤더로 전달하는 방식임

    • 실제 클라이언트 IP, 인증된 사용자명, mTLS 클라이언트 인증서 정보 같은 값은 백엔드가 믿어야 하는 데이터임
    • 그런데 HTTP에서는 이 값들을 결국 X-Real-IP 같은 헤더에 넣게 되고, 클라이언트가 보낸 헤더와 구조적으로 섞임
    • 프록시가 X-Real-IP를 지운다 해도 True-Client-IP 같은 다른 헤더를 미들웨어가 먼저 신뢰하면 공격자가 조작한 값이 백엔드까지 들어갈 수 있음
  • FastCGI는 이 부분에서 클라이언트 헤더와 프록시 신뢰 정보를 구조적으로 분리함

    • 클라이언트가 보낸 HTTP 헤더는 HTTP_ 접두사가 붙은 파라미터로 전달됨
    • 반면 실제 클라이언트 IP는 REMOTE_ADDR 같은 표준 파라미터로 전달되기 때문에, 클라이언트가 보낸 헤더가 신뢰 정보처럼 위장하기 어려움
    • Go의 net/http/fcgiREMOTE_ADDR를 자동으로 http.Request.RemoteAddr에 채워줘서 별도 미들웨어 없이도 동작한다고 함

중요

> 저자가 보는 FastCGI의 진짜 장점은 속도가 아니라 보안 모델임. 요청 경계와 신뢰 데이터의 출처가 프로토콜 레벨에서 분리돼 있다는 게 포인트다.

  • 물론 FastCGI가 만능은 아님. 30년 된 기술답게 단점도 선명함

    • WebSocket을 지원하지 않음
    • curl로 FastCGI 서버에 바로 요청을 보낼 방법도 없음. 저자는 curl이 FTP, Gopher, SMTP까지 지원하면서 FastCGI는 안 된다고 꼬집음
    • Go FastCGI 서버를 여러 리버스 프록시 뒤에서 벤치마크했을 때 일부 워크로드는 HTTP/1.1이나 HTTP/2보다 처리량이 낮았다고 함
    • 다만 저자는 이게 프로토콜 자체의 한계라기보다 FastCGI 코드 경로가 HTTP만큼 최적화되지 않았기 때문일 수 있다고 봄
  • 결론은 꽤 현실적임. WebSocket이 필요 없고 성능 병목이 아니라면 FastCGI는 지금도 충분히 쓸 만하다는 것

    • 저자는 SSLMate에서 FastCGI를 10년 넘게 프로덕션에서 쓰고 있다고 밝힘
    • 병목이 생기면 HTTP 리버스 프록시의 보안 지뢰밭을 감당하기보다 차라리 하드웨어를 더 사겠다는 입장임

기술 맥락

  • 여기서 선택지는 “프록시와 백엔드 사이도 HTTP로 말할 것인가, 아니면 FastCGI 같은 전용 프로토콜을 쓸 것인가”예요. 보통은 관성적으로 HTTP를 고르지만, 저자는 이 구간에서는 범용성보다 메시지 경계와 신뢰 정보 분리가 더 중요하다고 보는 거예요.

  • HTTP/1.1이 위험해지는 이유는 요청의 끝을 메시지 내부 값으로 판단하기 때문이에요. 프록시와 백엔드가 Content-Length, Transfer-Encoding 같은 단서를 다르게 해석하면 같은 바이트 스트림이 서로 다른 요청으로 보일 수 있거든요.

  • FastCGI는 요청을 레코드 단위로 나누고, 클라이언트 헤더와 프록시가 추가한 환경 정보를 다른 이름 공간으로 보냅니다. 그래서 X-Real-IP를 지웠는지, 대소문자 변형까지 처리했는지 같은 방어 로직에 덜 기대게 돼요.

  • 트레이드오프도 분명해요. WebSocket이 필요하거나 HTTP 도구 생태계에 깊게 기대는 서비스라면 FastCGI 전환이 번거로울 수 있어요. 반대로 일반적인 요청-응답 백엔드이고 앞단 보안 경계가 중요하다면, 오래된 프로토콜이라는 이유만으로 제외할 선택지는 아니라는 얘기예요.

요지는 '오래된 기술이라 낡았다'가 아니라, 특정 문제를 풀기 위해 설계된 프로토콜이 범용 HTTP보다 나을 수 있다는 얘기다. 백엔드 앞단을 HTTP로만 붙이는 게 당연하다고 생각했다면 한 번쯤 아키텍처 기본값을 의심해볼 만하다.

댓글

댓글

댓글을 불러오는 중...

backend

삼성SDS, 삼성전기 SAP ERP 클라우드 전환 — 다운타임 140시간을 34시간으로 줄였다

삼성SDS가 삼성전기의 차세대 SAP ERP 클라우드 전환 프로젝트를 완료. 국내 최초 RISE with SAP 프리미엄 서플라이어 기반 사례이고, Downtime Optimized Conversion 적용으로 8.5TB HANA DB 전환 다운타임을 76% 단축. DVM으로 DB 용량 35% 축소, 업무 효율 25% 이상 개선.

backend

월 $20 인프라로 MRR $10K 회사 여러 개 돌리는 법

VPS 하나, Go 바이너리, SQLite, 로컬 GPU, GitHub Copilot 조합으로 월 $20 이하의 인프라 비용으로 MRR $10K 넘는 회사를 여러 개 운영하는 개발자의 실전 플레이북. AWS 없이도 충분히 확장 가능한 아키텍처를 구축할 수 있음을 구체적 수치와 코드로 보여줌.

backend

소프트웨어 개발자를 위한 USB 입문 — 유저스페이스 드라이버 직접 만들기

커널 코드 없이 libusb를 사용해 유저스페이스에서 USB 드라이버를 작성하는 방법을 안드로이드 Fastboot 프로토콜을 예시로 설명하는 튜토리얼. USB 엔드포인트 유형, 열거(enumeration) 과정, 컨트롤/벌크 전송의 동작 원리를 실습 중심으로 다룸.

backend

IBM과 Arm, 양쪽 워크로드 모두 실행 가능한 하드웨어 공동 개발 발표

IBM과 Arm이 클라우드 이전이 어려운 규제 워크로드를 겨냥해 양쪽 아키텍처를 모두 지원하는 하드웨어를 공동 개발함. 가상화, 규제 준수, 공통 기술 세 가지에 초점을 맞추고 있으며, 출시까지 약 3년이 소요될 것으로 예상됨.

backend

AWS 엔지니어, Linux 7.0에서 PostgreSQL 성능 절반으로 떨어지는 회귀 보고 — 수정도 쉽지 않은 상황

AWS 엔지니어가 Linux 7.0 커널의 프리엠션 모드 변경으로 PostgreSQL 처리량이 0.51x로 떨어지는 심각한 회귀를 보고했음. 커널 메인테이너는 되돌리기 대신 PostgreSQL이 RSEQ를 도입하라는 입장이라 논란 중.