[4] NetworkProcess missing IPC authorization on `WebPageProxyIdentifier`
A compromised renderer could borrow another WKWebView's cookie policy by name.
Medium — 이번 diff는 process 간 insecure direct object reference를 차단합니다. 다만 단독으로 성립하는 버그가 아니라 second-stage 버그입니다. 이미 장악된 WebContent process가 필요하고, 피해 page가 relaxed third-party cookie blocking을 opt-in한 상태여야 합니다. 두 조건이 모두 갖춰지지 않으면 identifier가 완화된 policy를 선택하지 못하고, 권한 확장도 발생하지 않습니다.
WebKit의 NetworkProcess는 IPC를 통해 각 WebContent process의 cookie 조회, resource load, WebSocket channel 생성을 처리합니다. 여기서 관련된 구성 요소는 두 가지입니다. 먼저 WebPageProxyIdentifier는 UIProcess가 생성하고 소유하지만, NetworkProcess에는 WebContent가 보낸 메시지에 담겨 전달됩니다. 다른 하나는 process 전역에서 page 단위로 관리되는 policy 집합으로, third-party cookie blocking이 완화된 page 목록을 기록합니다. 빠져 있던 invariant는 간단합니다. 하나의 connection은 자신이 실제로 권한을 가진 page만 지칭할 수 있어야 합니다.
관전 포인트: 장악된 WebContent process는 다른 page의 WebPageProxyIdentifier를 메시지에 넣을 수 있습니다. 다른 process에 속한 별도 WKWebView의 identifier도 예외가 아닙니다. 이 경우 해당 page의 완화된 third-party cookie policy가 공격자 자신의 요청에 적용되고, cross-origin cookie가 노출될 수 있습니다.
Commit message에 따르면, NetworkProcess는 renderer가 보낸 identifier를 신뢰할 수 있는 key로 그대로 사용했습니다. 송신 connection이 해당 page를 지칭할 권한이 있는지는 확인하지 않았습니다. 기존의 allowsFirstPartyForCookies(m_webProcessIdentifier, firstParty) check는 page가 아니라 registrable domain 단위로 동작합니다. 그래서 이번 수정에서는 process가 특정 page의 first-party cookie 접근 권한을 받는 시점에 해당 identifier도 함께 허용하도록 했습니다.
Source/WebKit/NetworkProcess/NetworkConnectionToWebProcess.cpp
Source/WebKit/UIProcess/WebPageProxy.cpp
Patch Details
이번 변경으로 process별 allow-list인 m_allowedWebPageProxyIdentifiers가 추가되었습니다. 이 목록은 새로 추가된 WebPageProxy::addAllowedFirstPartyForCookies wrapper를 통해 채워집니다. 즉 process가 특정 page의 first-party cookie 접근 권한을 받을 때 identifier도 함께 등록됩니다. 기존의 navigation/process-swap 호출 지점은 모두 이 wrapper를 사용하도록 변경되었습니다. 이후 NetworkProcess는 renderer가 보낸 page identifier를 받아들이기 전에 validateCookieAccess, scheduleResourceLoad, performSynchronousLoad, createSocketChannel에서 allow-list를 확인합니다.
IPC trust boundary에서 client가 보낸 identifier를 권한 증표(capability)로 그대로 받아들이고, 송신 측이 이를 지칭할 권한이 있는지는 확인하지 않는 패턴.
Background
4-process 모델. WebKit은 UIProcess, WebContent, Networking, GPU process로 나뉩니다. 이 중 WebContent는 신뢰할 수 없는 process입니다. 반면 NetworkProcess는 실제 요청을 전송하고 policy 상태를 보관합니다.
WebPageProxyIdentifier. UIProcess가 생성하고 소유하는 identifier입니다. 그러나 NetworkProcess로는 WebContent가 보내는 메시지(getRawCookies, cookiesForDOM, scheduleResourceLoad, createSocketChannel, ...)의 일반 필드로 전달됩니다.
Third-party cookie blocking 완화. _shouldRelaxThirdPartyCookieBlocking은 WKWebViewConfiguration의 opt-in 옵션입니다. 평소 NetworkProcess는 firstParty와 url이 cross-site이면 cookie 제공을 거부하는데, 이 옵션은 그 제한을 완화합니다. 이때 어떤 요청에 완화를 적용할지는 page identifier로 결정됩니다.
Analysis
이 버그는 memory-safety 문제가 아닙니다. IPC trust boundary에서 authorization check가 누락된 경우로, insecure direct object reference의 process 간 버전에 해당합니다.
diff에서는 key 구조가 명확히 드러납니다. NetworkProcess::createNetworkConnectionToWebProcess는 m_pagesWithRelaxedThirdPartyCookieBlocking.addAll(parameters.pagesWithRelaxedThirdPartyCookieBlocking)을 호출합니다. 대상 컨테이너는 singleton NetworkProcess에 있는 Vector<WebPageProxyIdentifier>입니다. 즉 connection별로 분리되지 않고 모든 WebContent connection이 같은 상태를 공유합니다.
page identifier가 완화 여부를 결정한다는 점은 cookieRequestHeaderFieldValueDigest 위에 남아 있는 context line에서 확인됩니다: "The only caller never passed frame, page or web page proxy identifiers, so relaxed third-party cookie blocking never applied here." 문제는 이 identifier가 renderer로부터 전달되면서도 그 renderer와 대조된 적이 없었다는 점입니다. 따라서 장악된 WebContent process가 임의 page의 identifier를 넣으면, 완화된 policy가 자신의 요청에 적용될 가능성이 있었습니다. 다만 page 단위 완화 조회 방식, identifier 추측 가능성, loader/socket 경로에서의 귀속 오류는 예상 시나리오에 해당합니다. 실제로 page 간 정보 노출이 성립하려면 두 조건이 필요합니다. 피해 page가 _shouldRelaxThirdPartyCookieBlocking을 opt-in했어야 하고, 공격자가 해당 page의 identifier를 지칭할 수 있어야 합니다.
수정 방식이 page 단위 equality 비교가 아니라 process별 set인 데에는 이유가 있습니다. Site isolation 환경에서는 하나의 page가 정상적으로 여러 process에 걸쳐 존재합니다. main frame과 out-of-process subframe이 서로 다른 process에 있을 수 있기 때문입니다. 결과적으로 소유 관계는 다대다가 됩니다. identifier 허용을 first-party cookie 접근 권한 부여에 통합한 것도 같은 맥락입니다. 이렇게 하면 process가 이미 해당 page의 cookie 권한을 가진 경우에만 identifier를 지칭할 수 있게 됩니다.
Audit directions
- Entitlement check 없이 capability로 쓰이는 renderer 제공 identifier. 메시지 본문에서 꺼낸
WebPageProxyIdentifier,FrameIdentifier,PageIdentifier를 key로 process 전역 policy를 조회하는 NetworkProcess message receiver라면 allow-list check가 필요합니다. page/frame identifier를 읽고 공유 policy 상태를 참조하는 다른NetworkConnectionToWebProcessreceiver도 점검할 필요가 있습니다. 코드 리뷰에서는 메시지에서 decode한 identifier로NetworkProcesssingleton map을 조회하면서m_allowed*소속 여부를 확인하지 않는 receiver가 눈여겨볼 신호입니다. - Process 단위 vs. connection 단위 policy 상태.
addAll(parameters....)가 client가 제공한 집합을 process 전역 컨테이너에 합치는 지점을 살펴볼 필요가 있습니다. 이때 권한 기준 축이 site isolation의 다대다 소유 관계와 일치하는지 확인해야 합니다.