← All issues

Cross-origin parent DOM exposed via NavigateEvent.sourceElement

A cross-origin iframe was handed a live pointer to its parent's DOM

0beaf19

LayoutTests/http/wpt/navigation-api/resources/sourceElement-cross-origin-leak-frame.html

+navigation.addEventListener("navigate", event => {
+ const source = event.sourceElement;
+ let leakedEmail = null, leakedAccountId = null;
+ if (source) {
+ try { leakedEmail = source.ownerDocument.querySelector("#profile-email").textContent; } catch (e) {}
+ try { leakedAccountId = source.ownerDocument.querySelector("#account-panel").dataset.accountId; } catch (e) {}
+ }
+ window.parent.postMessage({ sourceElementIsNull: source === null, leakedEmail, leakedAccountId }, "*");
+});

Source/WebCore/page/Navigation.cpp

+#include "NodeDocument.h"
// (수정: ownerDocument가 relevant document와 same-origin-domain이 아닌 경우 sourceElement를 null로 처리)

Navigation API는 같은 document 내의 navigation을 가로채고 제어하는 현대적인 web platform API입니다. Navigation이 발생하면 해당 realm에 NavigateEvent가 전달되며, sourceElement 프로퍼티로 어떤 element가 navigation을 시작했는지 확인할 수 있습니다. 이 API는 same-origin을 가정한 설계로, sourceElement는 양측이 같은 origin을 공유할 때에만 의미가 있습니다.

이 commit은 parent frame의 element가 target= 속성으로 cross-origin child iframe을 navigate하는 상황을 수정합니다. 이때 event는 child의 realm에서 발생하지만, 기존 구현은 child에게 parent DOM element의 live reference를 그대로 넘겨주었습니다. 결과적으로 ownerDocument를 통해 cross-origin document 전체를 제한 없이 탐색할 수 있었습니다.

수정은 innerDispatchNavigateEvent에 적용되었습니다. 이 함수는 form-submitter/form rewrite 이후의 중앙 dispatch 경로입니다. sourceElement의 owning document가 navigation 대상과 same-origin-domain이 아닌 경우, sourceElement를 null로 처리합니다. anchor·form·form-submitter·download navigation 경로 전체에 일관되게 적용됩니다.

Parent (origin A)                        iframe (origin B)
  <a href="..." target="iframe">           navigation.addEventListener("navigate", e => {
    [click] ──────────────────────────►      e.sourceElement            // was parent <A> element
                                             .ownerDocument             // ← full parent Document
                                             .querySelector("#email")   // ← PII extracted
                                           });

After fix (innerDispatchNavigateEvent):
  if (!sourceElement->document().isSameOriginDomain(relevantDocument))
      sourceElement = nullptr;   // ← enforced before event reaches child

이는 별도의 사용자 조작 없이 일반적인 페이지 사용만으로도 동작하는 Same-Origin-Policy bypass입니다. parent frame element가 navigation을 유발할 때마다 cross-origin iframe이 parent document의 내용(이메일, 계정 ID, DOM에 포함된 인증 토큰 등)을 조용히 추출할 수 있었습니다. WPT 테스트에서 이미 known baseline FAIL로 표시된 상태였기 때문에, spec을 주시하는 개발자들에게 regression으로 노출된 채 배포된 것입니다.

여러 방향에서 검토할 필요가 있습니다. same-origin-domainsame-origin보다 약한 조건으로, document.domain 완화를 허용합니다. 이를 통해 두 페이지가 협력하여 origin을 확장할 수 있습니다. document.domain을 설정한 parent가 이 검사를 통과하는 경우, spec에서 안전하지 않다고 볼 iframe에 sourceElement가 다시 노출되는지 확인해야 합니다.

수정이 form-submitter/form rewrite 이후에 위치하므로, 이 시점 이전의 중간 표현이나 early-exit 경로에서 null 처리 없이 sourceElement가 event 객체에 노출될 수 있는지도 확인해야 합니다.

테스트는 SiteIsolationEnabled=false 조건에서만 실행됩니다. 따라서 process-isolated cross-origin iframe 환경에서는 수정이 검증되지 않았습니다. full site isolation 환경에서는 검사 동작이나 dispatch 경로가 달라질 가능성이 있습니다.

마지막으로 NavigateEvent의 다른 프로퍼티들, 즉 destination.url, info, AbortSignal에서 유사한 cross-origin 정보 유출이 있는지 확인해야 합니다. same-origin 가정으로 설계된 API가 cross-origin으로 전달되는 패턴은 여러 프로퍼티에 걸쳐 반복적으로 나타날 수 있습니다.