정의
서버가 "사용자가 준 주소로 대신 요청을 보내주는" 기능(이미지 미리보기, 링크 썸네일 등)을 만들 때, 공격자가 그 주소를 서버 내부용 주소로 바꿔치기해 몰래 접근하는 공격을 막는 방법이다.
용어부터 풀면: fetch는 서버가 어떤 URL로 HTTP 요청을 보내 내용을 가져오는 것, allowlist(허용 목록)는 "이 호스트들만 요청 허용" 화이트리스트, **스킴(scheme)**은 URL 맨 앞의 http:·https:·file: 같은 프로토콜 종류다.
정확히 말하면 SSRF(Server-Side Request Forgery — 서버 측 요청 위조) 는 공격자가 서버에게 의도하지 않은 URL을 fetch하게 만드는 취약점이다. 이미지 리사이즈 프록시, OG fetcher(링크 공유 시 미리보기용으로 대상 페이지의 메타데이터를 가져오는 서버), webhook relay(외부 이벤트 알림을 대신 전달하는 서버)처럼 ?url= 파라미터를 받아 그 주소로 요청하는 서버 엔드포인트는 모두 이 위험에 노출된다.
왜 필요한가
브라우저(클라이언트)는 외부에서 회사 내부망에 직접 요청을 못 보내지만, 서버는 보낼 수 있다. 서버는 내부 네트워크·사설 IP, 그리고 클라우드 메타데이터 주소(클라우드가 자기 인스턴스의 자격증명·설정을 응답해주는 특수 내부 주소, AWS의 경우 169.254.169.254)에도 접근할 수 있다.
그래서 클라이언트가 직접 못 치는 주소를 서버 프록시를 경유해 때릴 수 있게 방치하면, 공격자가 내부 데이터 유출·내부망 포트 스캔에 서버를 도구로 쓸 수 있다. "우리는 공개 이미지 URL만 받는다"는 가정만으로는 이걸 못 막는다 — 받은 URL이 정말 그런지 검증해야 한다.
동작 원리
- URL 파싱 → 스킴이
http:/https:인지 확인. - Hostname allowlist — suffix 매칭(예:
.cdn.example.com,.storage.provider) 또는 명시 host 목록. - 거부 시 400 + 거부된 hostname을 본문에 포함(운영 디버깅).
- (권장) private IP·link-local·metadata IP 차단, 리다이렉트 hop마다 host 재검증.
- 환경 변수로 배포별 suffix 추가 — 코드 변경 없이 새 CDN 도메인 허용.
실무 적용
- 이미지 프록시: 허용 스토리지/CDN suffix만 통과, fetch는 전체 presigned URL(서명 포함)로 수행하되 캐시 키는 pathname만 사용하는 식으로 분리.
- 에러 응답:
{ error: "Host not allowed", host: "evil.example" }— allowlist 누락을 즉시 확인. - 배포 체크리스트: 새 object storage 도메인 추가 시 env + allowlist 동시 갱신.
트레이드오프
| 선택 | 이득 | 비용 |
|---|---|---|
| Suffix allowlist | 운영 단순 | subdomain 우회 패턴 설계 필요 |
| Denylist만 | 빠른 도입 | 우회·리다이렉트에 취약 |
| 리다이렉트 비활성 | 안전 | 일부 CDN 동작 깨질 수 있음 |
사용하면 안 되는 경우
- 사용자 임의 URL fetch를 "편의상" allowlist 없이 허용.
- 내부 디버그용으로 allowlist를
*(모든 호스트 허용)로 풀어둔 것을 프로덕션에 그대로 유지.
흔한 실수
- 코드에 host를 추가했는데 옛 프로덕션 빌드(
next start)가 떠 있어 반영 안 됨 — 프로세스 시작 시각·커맨드 확인. - presigned URL의 서명 쿼리와 host 검증을 혼동 — host는 allowlist, fetch URL은 전체.
evil.amazonaws.com.attacker.com같은 suffix 매칭 버그 —host.endsWith('.amazonaws.com')규칙 검토.
관련 개념
- presigned-url-direct-upload — 클라이언트 직접 업로드 vs 서버 fetch
- server-image-proxy-transcoding-cache — 이미지 프록시에서의 가드 적용