← All issues

[1] CSP bypass in sandboxed srcdoc iframes

Severity: High | Component: WebCore loader / DocumentWriter | b4390e8

관찰 가능한 영향이 sandboxed srcdoc iframe을 포함하는 모든 문서에서의 script injection / CSP bypass primitive에 해당하며, 분석 신뢰도는 0.92입니다. 기존 WPT 실패 케이스 네 건(location 변경, window.open, form submission 2건)은 commit 수준에서 확인된 근거로, 동일한 단일 조건이 더 넓은 범위의 잘못된 policy 귀속 사례를 유발하고 있었음을 시사합니다.

Source/WebCore/loader/DocumentWriter.cpp

- if (currentHistoryItem && currentHistoryItem->policyContainer()) {
+ if (triggeringAction && triggeringAction->type() == NavigationType::BackForward && currentHistoryItem && currentHistoryItem->policyContainer()) {
const auto& policyContainerFromHistory = currentHistoryItem->policyContainer();
ASSERT(policyContainerFromHistory);
document->inheritPolicyContainerFrom(*policyContainerFromHistory);

LayoutTests/http/tests/security/contentSecurityPolicy/iframe-srcdoc-import-bypass.html

+ iframe1.srcdoc = srcdocContent;
+ iframe2.srcdoc = srcdocContent.replaceAll(iframe1.id, iframe2.id);
+<iframe id="iframe1" sandbox="allow-scripts"></iframe>
+<iframe id="iframe2" sandbox="allow-scripts" srcdoc=""></iframe>

패치 이전의 DocumentWriter::begin()은 현재 HistoryItem에 non-null policy container가 존재하는 경우, 해당 값을 새 문서의 PolicyContainer로 상속했습니다. 이번 수정으로 이 조건이 좁아졌습니다. 이제 history 기반 상속은 triggeringAction이 존재하고 그 NavigationTypeBackForward인 경우에만 동작합니다. 그 외 navigation — sandboxed iframe에 대한 동적 srcdoc 변경 등 — 은 spec이 요구하는 initiator 기반 상속 경로로 처리됩니다. WPT의 inheritance-from-initiator expectation 파일에서 네 건의 결과가 FAIL→PASS로 전환되었습니다(location 변경, window.open, form submission 2건). 새로운 layout test도 추가되었는데, script로 srcdoc를 할당한 뒤 sandboxed iframe이 동적 import()로 cross-origin module을 로드하는 시나리오를 검증합니다.

non-back/forward navigation에서 잘못된 policy container 출처 선택: initiator 상속이 필요한 상황에서 history item 상속이 적용된 패턴.

PolicyContainer는 문서의 CSP, referrer policy, cross-origin policy를 하나로 묶는 WebCore 집합체입니다. HTML 스펙에 따르면 srcdoc 문서는 자체 네트워크 응답이 없으므로, navigation을 유발한 initiator 문서로부터 policy container를 상속해야 합니다. 반면 history traversal의 경우, 문서의 HistoryItem이 원래 policy container의 스냅샷을 보관하여 back/forward 시 동일한 policy로 문서가 복원됩니다. DocumentWriter::begin()은 새 문서 생성 초기 단계에서 실행됩니다. 해당 문서의 스크립트가 실행되기 전 시점에, 어떤 container를 채택할지 결정하는 역할을 담당합니다.

패치 이전에는 loader가 단순한 heuristic을 사용했습니다. "history policy container가 존재하면 그것을 사용한다"는 방식이었습니다. back/forward traversal에서는 이것이 spec에 부합하지만, script로 srcdoc를 변경하는 fresh navigation에서는 그렇지 않습니다. iframe에 저장된 history container에는 부모 문서의 CSP가 포함되어 있지 않기 때문입니다. 결과적으로 새 문서는 spec이 요구하는 엄격한 CSP 대신, 허용적이거나 비어 있는 policy를 상속받게 되었습니다.

🔒

The general mechanics of CSP-inheritance for sandboxed srcdoc iframes are walked through, with attention to which navigation forms previously consulted the wrong policy source and what that meant for an attacker holding only an HTML-injection primitive.

더 확인하려면 구독해 주세요

🔒

Several reusable audit patterns covering policy-container inheritance across multiple loader entry points and local-scheme document constructions, with concrete grep starting points.

더 확인하려면 구독해 주세요