[4] FrameLoader inherits empty registrable domain for about:blank popups
diff가 수정하는 문제는 어느 first-party 페이지에서든 window.open('about:blank')를 통해 도달할 수 있는 안정적인 third-party cookie blocking bypass입니다. 영향 범위는 memory safety가 아닌 privacy/tracking에 해당하므로 Medium으로 평가합니다.
about:blank popup이 cross-origin 요청을 보낼 때, 해당 popup의 firstPartyForCookies는 about:blank로 설정되어 있었습니다. about:blank의 registrable domain은 빈 문자열이기 때문에, thirdPartyCookieBlockingDecisionForRequest()가 ThirdPartyCookieBlockingDecision::None을 반환하여 third-party cookie blocking이 우회됩니다. main frame URL이 owner-inherited document인 경우, FrameLoader::updateFirstPartyForCookies()는 opener의 firstPartyForCookies를 상속해야 합니다.
Source/WebCore/loader/FrameLoader.cpp
LayoutTests/http/tests/resourceLoadStatistics/third-party-cookie-blocking-about-blank-popup.html
Patch Details
updateFirstPartyForCookies()는 기존에 frame의 firstPartyForCookies를 page->mainFrameURL()로 무조건 설정했습니다. 수정 후에는 조건 검사가 추가되었습니다. main frame URL이 SecurityPolicy::shouldInheritSecurityOriginFromOwner()의 대상인 경우, page->mainFrame().opener()를 LocalFrame으로 downcast하여 opener document의 firstPartyForCookies()를 조회하고, 해당 값을 대신 사용하도록 변경되었습니다.
document URL이 about:blank와 같이 owner-inherited scheme인 경우, first-party-for-cookies 필드에 opener/parent 상속이 반영되지 않는 패턴.
Background
firstPartyForCookies는 frame별로 관리되는 URL로, 외부로 나가는 요청에 기록됩니다. network layer는 이 URL의 registrable domain(eTLD+1)을 "first party"로 삼아 cookie 목적지가 first-party인지 third-party인지를 결정합니다. about:blank와 같은 opaque scheme의 경우, registrable domain은 빈 문자열이 됩니다. WebKit의 third-party cookie blocking(Intelligent Tracking Prevention)은 tracker로 분류된 목적지에 대한 cross-site subresource 요청의 cookie를 차단하는데, 이 결정은 thirdPartyCookieBlockingDecisionForRequest()를 통해 이루어집니다. HTML 명세에 따르면 about:blank document는 자신을 생성한 browsing context로부터 security origin을 상속받습니다. SecurityPolicy::shouldInheritSecurityOriginFromOwner(url)은 이 규칙을 따르는 URL scheme(about:blank, about:srcdoc, data: 등)에 대해 true를 반환합니다.
Analysis
페이지에서 about:blank popup을 열면, popup의 main frame URL은 그대로 about:blank가 됩니다. 이때 updateFirstPartyForCookies()는 popup frame의 firstPartyForCookies를 해당 URL로 설정합니다. 이후 popup이 cross-origin 요청을 보내면, network layer가 about:blank의 registrable domain을 계산하여 빈 문자열을 얻게 됩니다. thirdPartyCookieBlockingDecisionForRequest()는 빈 registrable domain을 None으로 처리합니다. 비교 대상이 될 의미 있는 first party가 존재하지 않으므로, "차단하지 않음"으로 판단하는 것입니다.
first-party 페이지 A에서 window.open('about:blank')를 호출하면 main frame URL이 about:blank인 popup을 얻을 수 있습니다. 이 popup 안에서 fetch('https://tracker.example/...', {credentials: 'include'})와 같이 credentialed cross-origin 요청을 전송합니다. 패치 이전에는 popup의 firstPartyForCookies가 about:blank였고 registrable domain이 빈 문자열이었기 때문에, tracker.example에 대해 이전에 설정된 third-party cookie가 요청에 그대로 첨부되었습니다. regression test에서 cookieName = 'aboutBlankTestCookie'를 통해 정확히 이 케이스를 재현하고 있습니다.
다만 이 수정은 LocalFrame을 통한 main-frame opener chain만 처리합니다. opener가 RemoteFrame인 경우(site isolation 환경), dynamicDowncast<LocalFrame>이 조용히 실패하고 기존의 빈 registrable domain URL이 그대로 사용됩니다. 추가적인 follow-up이 필요한 지점입니다.
이 vulnerability는 ITP와 resource-load-statistics가 의존하는 third-party cookie blocking의 신뢰 경계를 약화시켰습니다. 해당 보안 모델은 모든 top-level browsing context가 비어 있지 않은 안정적인 first-party registrable domain을 가진다는 가정을 전제로 합니다. 패치 이전에는 초기 document URL이 owner-inherited인 popup에서 이 불변 조건이 위반되었습니다. first-party-for-cookies는 security origin과 병렬적으로 관리되는 상태입니다. 특별한 origin 상속 규칙을 따르는 URL scheme이 있는 경우, cookie policy의 first party도 동일한 상속을 반영해야 합니다. about:blank, about:srcdoc, data:, sandboxed iframe, javascript: URL이 이에 해당합니다. 이 반영이 이루어지지 않으면 network layer가 조용히 "first party 없음" 상태로 동작하게 됩니다.
Audit directions
- URL의 registrable domain이나 문자열 형태를 기반으로 privacy/security 정책을 결정하는 코드 — 특히 URL이 owner-inherited인 document에 적용되는 경우.
registrableDomain()호출 지점,RegistrableDomain생성 코드,mainFrameURL()에 대한 문자열 비교를 모두 점검합니다.Source/WebCore와Source/WebKit전체에서mainFrameURL()과firstPartyForCookies를 검색하고, 각 위치에서SecurityPolicy::shouldInheritSecurityOriginFromOwner를 고려하는지 확인해야 합니다. - Site isolation 환경에서의 opener/parent 상속 미흡. 이 수정은
page->mainFrame().opener()를LocalFrame으로 downcast하며, opener가RemoteFrame인 경우 조용히 종료합니다. cross-process opener에서도 여전히 빈 registrable domain 경로를 타는지, 해당 경우에 동등한 상속이 UI/Network process에서도 수행되는지 확인해야 합니다.WebPageProxy,WebFrameProxy::opener, 그리고firstPartyForCookies를 전달하는 IPC 코드를 살펴봐야 합니다. - SecurityOrigin 상속과 FirstPartyForCookies 상속 간의 불일치.
srcdociframe,data:URL 내비게이션,javascript:URL 로드,allow-same-originsandboxed iframe,document.open()재초기화 등 다른 document 생성 경로를 점검합니다. 각 경로에서firstPartyForCookies가 일관되게 업데이트되는지 확인해야 합니다.Document::initSecurityContext,FrameLoader::didBeginDocument, 그리고setFirstPartyForCookies의 모든 호출 지점을 시작점으로 삼습니다. - "빈 registrable domain == 정책 미적용" fail-open 동작.
thirdPartyCookieBlockingDecisionForRequest와 관련 결정 함수들(storage access, partitioned cookies, third-party storage)을 점검하여, 빈 first-party registrable domain이None이 아닌 보수적인 방향으로 처리되는지 확인해야 합니다.