← All reports

[Site Isolation] Authorize cookie IPC against the sites a web process hosts

Site isolation gave iframes their embedder as first party, and its cookies too.

Component: WebKit Site Isolation | a5cb75b

Source/WebKit/NetworkProcess/NetworkConnectionToWebProcess.cpp

+bool NetworkConnectionToWebProcess::hostsDomain(const RegistrableDomain& domain) const
+{
+ // file: URL처럼 registrable domain이 없는 URL은 검사할 site가 없습니다.
+ return !m_hostedDomains || domain.isEmpty() || m_hostedDomains->contains(domain);
+}
...
+ // firstParty만으로는 부족합니다. site isolation에서는 cross-site iframe의 process에 embedder가 first party로 허용되므로,
+ // 이 검사가 없으면 embedder의 URL을 지정해 embedder 자신의 cookie를 얻을 수 있습니다.
+ if (allowUnhostedURL == AllowUnhostedURL::No && !hostsDomain(RegistrableDomain { url })) {
+ CONNECTION_RELEASE_LOG_ERROR(IPC, "%" PUBLIC_LOG_STRING ": Rejecting cookie access for a site this process does not host", messageName.characters());
+ return CookieAccess::Disallow;
+ }

Source/WebKit/UIProcess/WebProcessProxy.cpp

+std::optional<HashSet<RegistrableDomain>> WebProcessProxy::hostedDomains() const
+{
+ if (m_hostedSiteTracking != HostedSiteTracking::Recorded)
+ return std::nullopt;
+ auto domains = m_sharedProcessDomains;
+ for (auto& site : m_committedSites) {
+ if (!site.isEmpty())
+ domains.add(site.domain());
+ }
+ return domains;
+}

Site isolation 환경에서는 cross-site iframe이 별도의 WebContent process에서 실행됩니다. 이때 cookie store를 보유하는 주체는 network process 하나뿐입니다. 기존에는 network process가 cookie IPC(cookiesForDOM, setCookiesFromDOM, GetRawCookies, SetRawCookie, DeleteCookie, domCookiesForHost 등)를 승인할 때 renderer가 전달한 firstParty만 검사했습니다. 문제는 site isolation이 out-of-process iframe에 embedder의 domain을 허용된 first party로 의도적으로 부여한다는 점입니다. 반면 cookie는 url을 기준으로 선택되는데, 이 값은 전혀 검사되지 않았습니다.

이번 commit에서는 UI process가 process별 hosted-domain 집합(WebProcessProxy::hostedDomains)을 network process로 전송하도록 변경되었습니다. 그리고 validateCookieAccess는 요청 url의 registrable domain이 이 집합에 포함된 경우에만 접근을 허용합니다. cookieRequestHeaderFieldValueDigest는 AllowUnhostedURL::Yes를 통해 이 검사에서 제외되었습니다. 또한 domCookiesForHost가 std::optional로 응답하도록 수정되었습니다. 그 결과 WebCookieCache가 거부 응답을 빈 cookie jar로 cache하지 않게 됩니다. 다만 일부 경우에는 검사가 permissive(std::nullopt)하게 유지됩니다. site isolation이 꺼져 있을 때, process가 site isolation 용도로 사용되기 전, single-process mode, web archive, remote-worker process가 이에 해당합니다.

Before:
  iframe proc (evil.com) --IPC(firstParty=embedder, url=embedder)--> NetworkProcess
       validate(firstParty) OK --> returns embedder's cookies (HttpOnly too)

After:
  UIProcess: hostedDomains() --setHostedDomains--> NetworkProcess
  iframe proc --IPC(firstParty=embedder, url=embedder)--> validate(firstParty) OK
       hostsDomain(RegistrableDomain{url})?
         nullopt set | empty domain | contains()  --> allow
         otherwise                                --> Disallow (domCookiesForHost: nullopt)

패치 이전에는 compromise된 cross-site iframe process가 embedder의 URL을 지정해, HttpOnly를 포함한 해당 site의 cookie를 IPC로 읽거나 쓸 수 있었습니다. 가치가 높은 대상 위에 새로운 security boundary가 세워진 셈입니다. 다만 allowlist 방식으로 구현되었고, 의도적으로 fail-open되는 경로가 여러 개 포함되어 있습니다.