Skip to content

SSRF 검사를 직접 가져오기 한 곳으로 정리 - #112

Open
m-a-king wants to merge 3 commits into
mainfrom
fix/111-ssrf-single-check
Open

m-a-king wants to merge 3 commits into
mainfrom
fix/111-ssrf-single-check

Conversation

@m-a-king

@m-a-king m-a-king commented Oct 14, 2026 •

Copy link
Copy Markdown
Collaborator

한눈에

사내망 주소 검사를 extractor 가 직접 여는 연결 한 곳으로 정리

  • 이전: 같은 사내망 주소 검사를 세 번 함
    • 직접 가져오기의 매 단계, 렌더 호출 전, 렌더 결과 확인
    • 렌더 결과 확인은 https 주소만 보고 http 주소는 건너뜀
  • 직접 가져오기의 매 단계 검사만 남김
    • 사내망 대역 목록은 infra 저장소의 파일 하나를 읽음
  • 렌더 경로의 사내망 판정은 renderer 가 브라우저의 실제 연결 IP 로 함
    • extractor 는 renderer 의 응답 필드를 받아 같은 실패 코드로 바꿈
  • 사내망과 무관한 실패에 붙던 SSRF 실패 코드를 바로잡음
  • 머지하면 extractor 가 자동 배포되고 서버 방화벽 규칙이 걸림
    • renderer 주소만 예외로 열림
    • infra PR 이 먼저 머지돼야 함

Situation

원인 추가

  • SSRF 검사를 직접 가져오기 한 곳으로 정리 #111
  • 추가
    • 렌더는 직접 가져오기가 실패한 뒤에만 탐. 렌더 호출 전 검사는 같은 URL 을 두 번 보는 셈
    • 렌더 결과 확인은 렌더와 다른 시점의 이름 해석이라, 브라우저가 실제로 간 IP 와 답이 다를 수 있음

Task

범위

Action

서비스별 SSRF 방어 (세 PR 합본)

층 extractor (이 PR) renderer (TeamPiKi/renderer#69)
입구 형식 https·URL 형식, 매 redirect http·https·host 존재
앱 주소 판정 매 홉 보내기 전, 이름을 IP 로 풀어 판정 후 그 IP 로만 연결 매 응답 받은 뒤, 브라우저가 실제 연결한 IP 판정 (평문 http 만)
판정 기준 infra 내부 대역 정본 같은 정본
판정 실패 BLOCKED_HOST (확정·승격 없음) internal_address_hit, extractor 가 BLOCKED_HOST 로 번역
서버 벽 infra 블록. 예외는 공통 셋 + renderer 주소 infra 블록. 예외는 공통 셋
  • 앱 판정이 보내기 전과 받은 뒤로 갈리는 이유: 브라우저는 연결 IP 를 고정할 수 없음. 그 틈은 서버 벽이 막음
  • 서버 벽이 못 막는 컨테이너 자기 loopback 과 벽 예외 목적지는 각 서비스의 앱 판정이 맡음

내부 대역 정본 소비

  • 빌드가 shared-infra/contracts/internal-ranges.txt 를 classpath 에 실음 (proto 와 같은 자리)
  • 가드 생성 시점에 읽음. 파일이 빠진 빌드는 기동에서 실패
  • IPv4 를 담은 IPv6 는 JDK 가 Inet4Address 로 돌려줘 따로 풀지 않음 (테스트로 확인)
대역 예시 이전 이후
0.0.0.1·240.0.0.1 통과 차단
198.18.0.1 통과 통과
사설·loopback·링크 로컬·CGNAT·ULA 차단 차단

렌더 경로

  • 렌더 호출 전 재검사와 홉 재검사 삭제
  • renderer 가 internal_address_hit 를 참으로 보내면 BLOCKED_HOST 확정 실패
  • internal_address_hit 가 없는 응답(판정하지 않는 renderer)은 홉을 쓰지 않고 HEADLESS_UPSTREAM 일시 실패
    • 원인: 홉 재검사를 지웠으므로, renderer 보다 먼저 배포되거나 renderer 가 롤백되면 판정 없는 홉이 들어옴 (독립 리뷰 지적)
    • 결과: 머지 순서를 코드가 지킴. 순서가 어긋나면 렌더만 일시 실패
  • 홉 URL 은 baseUri 용 형식 판정만. http 홉은 원본 link 로 폴백

실패 code

경우 이전 이후
https 아닌 redirect BLOCKED_HOST, 승격 없음 MALFORMED_REDIRECT, 헤드리스로 승격
host 없는 주소 BLOCKED_HOST INVALID_URL
내부 대역으로 resolve BLOCKED_HOST BLOCKED_HOST
렌더가 내부 주소에서 응답 받음 홉 URL 재해석으로 https 홉만 BLOCKED_HOST BLOCKED_HOST

배포

  • 새 슬롯을 띄우기 전에 infra 서버 벽 블록을 부름
  • renderer 주소는 배포가 매번 계산하는 값을 그대로 예외로 넘김

Result

검증

  • 정본에서 100.64.0.0/10 을 빼면 가드 테스트 2건(100.64.0.1·100.100.100.200)이 실패함. 가드가 정본을 실제로 읽음
  • 판정 필드 확인을 끄면 새 테스트 1건이 실패함
  • 빌드한 JAR 에 contracts/internal-ranges.txt 가 실림
  • 위험: CI 는 infra PR 머지 전까지 실패함. 내부 대역 정본과 렌더 계약 필드가 infra main 에 없음
  • 위험: https 아닌 redirect 가 이제 헤드리스로 넘어감. 그 렌더의 SSRF 는 renderer 판정과 서버 벽이 맡음
    • 판정하지 않는 renderer 의 홉은 쓰지 않음 (위 필드 확인)

후속


연관 이슈

- 내부 대역 판정을 infra contracts/internal-ranges.txt 로 바꿈
  - 빌드가 정본을 classpath 에 실음. 없으면 가드를 만드는 빈 생성에서 기동 실패
  - 이전: JDK 주소 분류 + CGNAT·ULA·메타데이터 개별 판정
    - 결과: renderer 판정과 6개 대역에서 어긋남
- 렌더 호출 전의 첫 URL 재검사 삭제
  - 원인: 렌더는 직접 가져오기가 실패한 뒤에만 탐. 같은 URL 이 이미 매 홉 검사를 통과함
- 렌더 홉 URL 의 재검사 삭제
  - 이전: https 홉만 다시 이름 해석해 검사, http 홉은 건너뜀
  - 원인: 렌더와 다른 시점의 이름 해석이라 브라우저가 실제로 간 IP 와 어긋날 수 있음
  - renderer 가 실제 연결 IP 로 판정해 internal_address_hit 로 알리면 BLOCKED_HOST 로 번역
- https 아닌 redirect 를 MALFORMED_REDIRECT 로 내고 헤드리스로 승격
  - 이전: BLOCKED_HOST. 내부 주소가 아닌데 SSRF 로 집계되고 승격도 막힘
- host 없는 주소는 INVALID_URL
  - 이전: BLOCKED_HOST
- 판정을 끄는 대조: 정본에서 100.64.0.0/10 을 빼면 가드 테스트 2건 실패
- 배포가 infra 의 egress_wall.sh 로 서버 벽을 건 뒤 새 슬롯을 띄움
- 이 서비스 고유 예외는 renderer 주소 하나
  - 배포가 매번 계산하는 renderer 주소를 그대로 넘김. 포트·박스가 바뀌어도 같이 따라감
@m-a-king m-a-king added the fix 외부 가시적 결함 수정 label Oct 14, 2026
@m-a-king m-a-king linked an issue Oct 14, 2026 that may be closed by this pull request
@m-a-king m-a-king self-assigned this Oct 14, 2026
@coderabbitai

coderabbitai Bot commented Oct 14, 2026 •

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 3e9b21dd-90e9-4a87-9f12-5aaedf110823
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

Copy link
Copy Markdown

Discord 스레드 연동용 메타데이터입니다. discord-pr-bot 워크플로가 자동 생성하며, 수정·삭제하면 PR 과 Discord 알림 연동이 끊깁니다.

- 렌더 응답에 internal_address_hit 가 없으면 홉을 쓰지 않고 HEADLESS_UPSTREAM 일시 실패
  - 원인: extractor 가 홉 재검사를 지웠음. renderer 가 판정 전 버전이면(배포 순서가 어긋나거나 롤백) 내부 응답이 그대로 들어옴 (독립 리뷰 지적)
  - 계약이 이 필드를 optional 로 바꿔 판정 여부를 실음 (TeamPiKi/infra#138)
  - 결과: 머지 순서를 코드가 지킴. renderer 가 먼저면 안전, extractor 가 먼저면 렌더만 일시 실패
- 음성 대조: 확인을 끄면 새 테스트 1건 실패
m-a-king added a commit that referenced this pull request Oct 15, 2026
- 링크 추출 요청마다 기한을 ScopedValue 로 묶음
  - 기한: 계약의 호출자 read 타임아웃(55초)에서 여유 5초를 뺀 50초
  - 이미지 경로·단위 테스트는 기한이 묶이지 않아 그대로
- 기한이 지나면 헤드리스 렌더·LLM 단계를 새로 시작하지 않음
  - 원인: core 는 55초에 끊고 재요청함. 그 뒤 시작한 단계는 받을 곳이 없고 재요청과 renderer 단일 슬롯·LLM 비용을 나눠 씀
  - 실패는 일시 실패 UPSTREAM_ERROR(502)
  - 진행 중인 단계는 끊지 않음
  - 대안: 남은 시간이 렌더 예산(22초)보다 적으면 렌더를 시작하지 않음
    - 기각: LLM 뒤 필드 부족으로 렌더로 넘어가는 경로가 지금은 55초 안에 성공하는데 실패로 바뀜
  - 대안: core 가 남은 기한을 헤더로 보냄
    - 기각: core 의 read 타임아웃이 고정이라 헤더가 새로 주는 정보 없음. 배포가 세 repo 로 늘어남
- redirect 홉 사이에는 검사하지 않음
  - 원인: 홉은 renderer·LLM 을 쓰지 않음. 열려 있는 SSRF 정리 PR(#112)과 같은 파일
m-a-king added a commit that referenced this pull request Oct 15, 2026
- SiteConcurrencyLimiter 추가. 정적 fetch 를 사이트별 세마포어로 감쌈
  - 사이트는 공개 접미사 목록 기준 등록 도메인(guava InternetDomainName). www·m 이 한 사이트
  - 기본값: 사이트당 2개, 최대 대기 15초(fetch.site-limit.*)
  - 원인: 몰 요청이 고정 IP 하나에서 나가 몰의 IP 단위 제한에 걸리면 그 몰 전체가 막힘
- 자리를 못 얻으면 SiteBusyException(UPSTREAM_ERROR 일시 실패)
  - PageFetchException 이 아니라서 헤드리스로 넘어가지 않음
  - 원인: 넘기면 같은 사이트를 렌더 서버 IP 로 다시 요청해 상한이 무력해짐
- 제한 위치는 DefaultProductLinkExtractor 의 fetch 호출
  - 대안: PageFetcher 를 감싸는 @primary 빈
    - 기각: 통합 테스트의 @primary StubPageFetcher 와 충돌
  - 대안: HttpPageFetcher 의 redirect 홉마다 제한
    - 기각: 열려 있는 SSRF 정리 PR(#112)과 같은 파일. 홉 사이 사이트가 바뀌는 체인은 짧음
- 줄무늬 1024 개의 약한 참조 세마포어
  - 원인: 사이트 키가 늘어도 메모리가 자라지 않음
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

fix 외부 가시적 결함 수정

Projects

None yet

Development

Successfully merging this pull request may close these issues.

SSRF 검사를 직접 가져오기 한 곳으로 정리

1 participant