실습 전용 — 이 앱은 보안 통제를 의도적으로 비활성화합니다. 일회용 구독에서만 구동하고 실습 후 삭제하세요.
ADFS SAML Lab판별 중…Host 192.168.4.110:3000PUBLIC_BASE_URL https://adfs-lab.prism-proxy.localtest.me:8443base 소스 요청 유도X-Forwarded-* 있음피어 192.168.4.100경고 1요청 인스펙터 →

iframe 임베딩 하네스 (H-C)

고객사 내부 앱은 보통 자기를 iframe 에 못 넣게 막아 둡니다. 프록시가 그 헤더를 다시 써야 임베드가 됩니다 — 그 구현이 실제로 동작하는지 브라우저로 확인합니다.

읽는 법

  • onload 로는 판정할 수 없다 — 차단된 프레임도 about:blank 로 load 이벤트가 뜬다. 그래서 프레임이 부모로 보내는 postMessage 를 기다린다. 메시지가 오면 우리 문서가 실제로 렌더된 것이고, 타임아웃이면 브라우저가 막은 것이다.
  • 두 열로 읽는다 — 직접 접속에서는 막는 헤더가 그대로 살아 차단되는 것이 정상이고, 프록시 경유에서 통과해야 재작성이 동작하는 것이다. H-A 와 같은 구조다.
  • #10 만 기대값이 반대다 — IdP 는 프레임 렌더를 거부하는 것이 맞다(클릭재킹 방지). 거기서 로그인 화면이 뜨면 오히려 위험하다.
  • X-Frame-Options 와 CSP 는 처리 방식이 다르다 — 전자는 helmet 이 먼저 심어 두므로 res 에서 직접 지워야 하고, 후자는 값을 덮어써야 한다. 한쪽만 되는 상태가 실제로 있었다.

접속 경로

브라우저가 보는 주소

https://aws-insider-e1e444fd-dfae-480a-b782-5eaa35682f5e.feelanet.ai
요청 유도

케이스 10개. 임베드 대상은

/api/lab/frame
이고 쿼리로 막는 헤더를 고릅니다.

프록시 쪽

PROXY_FRAME_ANCESTOR_ORIGINS
가 비어 있으면 아무도 임베드할 수 없습니다 — 그 경우 #2·#4·#6 이 실패합니다.

통과 0 · 실패 0 / 전체 10
#1 기준선 — 막는 헤더 없음임베드 허용

아무 제한도 없는 페이지를 임베드한다. 이것이 실패하면 하네스 자체가 고장이다.

/api/lab/frame?token=baseline

#2 X-Frame-Options: DENY차단 재현

가장 흔한 차단 방식. 프록시가 이 헤더를 지워야 임베드된다. 직접 접속에서는 차단되는 것이 정상이다.

★ 프록시가 res.removeHeader('X-Frame-Options') 를 하는 이유가 이 케이스다.

/api/lab/frame?token=xfo-deny&xfo=deny

#3 X-Frame-Options: SAMEORIGIN차단 재현

같은 오리진에서만 허용. 프록시 경유로 보면 페이지와 프레임이 같은 오리진이라 지우지 않아도 통과할 수 있다 — 그래서 이 케이스만으로는 재작성 여부를 못 가른다.

/api/lab/frame?token=xfo-sameorigin&xfo=sameorigin

#4 CSP frame-ancestors 'none'차단 재현

X-Frame-Options 의 후속 표준. 프록시가 이 값을 다시 쓰지 않으면 차단된다. CSP 는 delete 가 아니라 **값을 새로 세팅**해야 한다는 것이 구현 주석의 요지다.

★ X-Frame-Options 는 res 에서 직접 지워야 하고(helmet 이 먼저 심어 둔다), CSP 는 값을 덮어써야 한다. 처리 방식이 달라서 한쪽만 되는 상태가 실제로 있었다.

/api/lab/frame?token=csp-none&csp=none

#5 CSP frame-ancestors 'self'차단 재현

같은 오리진만 허용. 프록시 경유에서 어떻게 다시 쓰이는지 본다.

/api/lab/frame?token=csp-self&csp=self

#6 CSP frame-ancestors 가 남의 origin 만 허용차단 재현

우리를 명시적으로 배제한 정책. 프록시가 허용 목록으로 덮어써야 임베드된다.

/api/lab/frame?token=csp-foreign&csp=https%3A%2F%2Fexample.com

#7 Sec-Fetch-Dest 가 iframe 으로 오는가헤더 관찰

프록시는 이 헤더로 '프레임 안의 내비게이션' 을 판별해 로그인 리다이렉트 대신 안내 페이지를 준다(prism-proxy.middleware.ts). 중간에서 떨어지면 그 분기가 죽는다.

/api/lab/frame?token=sec-fetch-dest

#8 프레임 서브리소스 요청에 쿠키가 실리는가쿠키 전달

SameSite=Lax 쿠키는 교차 사이트 프레임 요청에 실리지 않는다. 그래서 세션 쿠키가 SameSite=None; Secure 여야 한다 — B-4 와 같은 뿌리의 문제다. 서버가 본 쿠키를 돌려받아 확인한다(document.cookie 는 HttpOnly 를 못 본다).

SameSite=None 쿠키도 브라우저의 서드파티 쿠키 차단(Safari ITP, Chrome 시크릿) 앞에서는 무력하다. 이 한계는 코드로 못 없앤다.

/api/lab/frame?token=frame-cookie

#9 중첩 프레임중첩/격리

프레임 안의 프레임. frame-ancestors 는 **조상 전체**를 보므로, 한 단계만 허용하면 중첩에서 막힌다. 임베드 위에 또 임베드하는 구성이 있으면 여기서 드러난다.

/api/lab/frame?token=nested-outer

#10 ADFS 로그인을 프레임에 띄우기중첩/격리차단되어야 통과

IdP 는 프레임 렌더를 거부한다(클릭재킹 방지). **차단되는 것이 정상**이고, 그래서 프록시가 프레임 안에서는 302 대신 안내 페이지를 준다. 여기서 로그인 화면이 뜨면 오히려 IdP 설정이 위험한 것이다.

★ 이 케이스는 **차단되어야 통과**다. 기대값이 반대인 유일한 케이스.

/api/saml/login?returnTo=/lab/iframe

띄운 프레임들

차단된 것은 빈 칸으로 남습니다