방어 방법
- 허용 목록(allowlist) — 미리 정해둔 도메인/스킴만 요청 허용. 사설 IP·loopback·link-local 대역은 차단.
- DNS 재바인딩 주의 — 도메인 검증 후 실제 요청 시점에 IP가 바뀔 수 있으므로, 연결 직전에도 다시 확인.
- 리다이렉트 금지 또는 재검증 — 정상 URL이 302로 내부 IP로 튕기는 우회를 막는다.
- 네트워크 분리 — 이 기능을 처리하는 서버는 애초에 내부망 접근 권한이 없는 별도 네트워크에 둔다(egress 방화벽).
# 방어: 스킴 + 사설 IP 차단
import ipaddress, socket
from urllib.parse import urlparse
def is_safe_url(url: str) -> bool:
p = urlparse(url)
if p.scheme not in ("http", "https"):
return False # file:// gopher:// 등 차단
ip = ipaddress.ip_address(socket.gethostbyname(p.hostname))
if ip.is_private or ip.is_loopback or ip.is_link_local:
return False # 169.254.x.x, 127.x, 10.x 등
return True
토글을 켜고 다시 "내부망" 프리셋들을 눌러보면, 서버가 요청을 보내기 전에 차단하는 걸 확인할 수 있다.