Local Network Access stops trusting the web process's client description
A compromised web process could talk its way onto the local network, no prompt shown.
Component: WebKit NetworkProcess | 87b3951
Source/WebKit/NetworkProcess/NetworkResourceLoader.cpp
Local Network Access(LNA)는 더 public한 address space에서 더 private한 address space(local, loopback)로 향하는 요청에 permission prompt를 요구합니다. 다만 same-origin load와 top-level load는 예외로 처리됩니다. 문제는 요청을 시작한 document에 대한 정보를 web process가 직접 NetworkResourceLoadParameters에 직렬화해 전달했다는 점입니다. 결과적으로 이 검사는 attacker가 제어하는 입력을 근거로 policy를 결정하는 셈이었습니다.
이번 변경으로 NetworkProcess가 각 frame의 document를 생성한 response의 address space와 origin을 직접 기록하게 되었습니다. 이후 web process가 보낸 정보는 이 기록과 대조됩니다. 이때 web process의 주장은 요청을 더 엄격하게 만드는 방향으로만 반영되고, origin이 일치하지 않으면 opaque origin으로 처리됩니다. 한편 secure-context flag는 UI process의 first-party 검사를 통과해야 설정되도록 변경되었습니다. top-level 예외 역시 자신의 first party에서 시작된 load에만 유지됩니다. Shared worker와 service worker는 frame 기록이 없기 때문에 현재로서는 거부됩니다. 다만 permissions-policy 상태는 여전히 web process가 제공합니다(bug 326009).
Significance
패치 이전에는 compromised web process가 loopback에 있다고 주장하거나, 대상의 origin을 자신의 origin으로 주장하거나, subresource를 main-frame load로 표시하는 방식으로 prompt 없이 local network에 도달할 수 있었습니다.
Audit directions
이런 종류의 메커니즘에서는 버그가 주로 경계 지점에 숨어 있습니다. 먼저 recordLocalNetworkAccessFrame과 browsingContextRemoved에서 기록의 lifecycle을 살펴볼 필요가 있습니다. stale 기록, frame ID 재사용, 잘못된 connection 아래에 기록되는 경우가 점검 대상입니다.
다음으로 기록을 남기지 않는 main-resource 경로가 있는지 확인해야 합니다. 현재 알려진 기록 지점은 continueDidReceiveResponseAfterLocalNetworkAccessCheck, continueDidRetrieveCacheEntryAfterLocalNetworkAccessCheck, transferToNewWebProcess 세 곳입니다. 그 외에 redirect나 service worker가 가로챈 load처럼 기록이 누락되는 경로를 찾아볼 만합니다.
또한 localNetworkAccessFrameRecord의 parent-frame fallback도 점검이 필요합니다. 이 fallback 때문에 about:blank, srcdoc, blob:, data:, sandboxed document가 부모의 기록으로 판정되는지 살펴봐야 합니다. 조작된 navigationRequester->frameID가 어떤 기록을 선택하는지, SecurityOriginData의 비교 semantics가 어떤지도 확인 대상입니다. web process가 영향을 줄 수 있는 firstPartyForCookies의 범위와 redirect가 originalRequest()와 상호작용하는 방식도 함께 볼 필요가 있습니다.
기록이 없는 경우는 fail closed로 처리됩니다. 즉 핵심 질문은 stale하지만 일치하는 기록이 fail open으로 이어질 수 있는가입니다.