[1] Validate connection access to FileSystem storage with FileSystemHandleIdentifier
Sixteen storage handlers took an integer as proof you owned the file.
High. 열여섯 개의 storage message handler가 정수 하나를 권한 증명으로 받아들였습니다. 그 결과 하나의 사이트에만 국한되어야 할 renderer가 같은 세션 내 다른 모든 사이트의 private filesystem을 읽고, 덮어쓰고, 삭제할 수 있었습니다. 메모리 손상은 필요하지 않습니다. Authorization 계층에서 이미 escalation이 완성되는 구조이기 때문입니다.
Origin Private File System은 각 web origin에게 navigator.storage.getDirectory()로 접근 가능한 private하고 영속적인 filesystem을 제공합니다. WebKit에서는 실제 파일이 network process에 존재하며, web content는 proxy만 갖습니다. 디렉터리와 파일은 IPC 상에서 FileSystemHandleIdentifier라는 이름으로 식별됩니다. 이는 handle이 열릴 때 network process가 발급하는 opaque한 정수로, 이후 모든 operation에서 그대로 인용됩니다. Network process는 이 구조에서 신뢰받는 중재자 역할을 맡습니다. WebContent connection이 자신에게 허용된 사이트의 storage에만 접근하도록 보장하는 것이 network process의 책임입니다.
관전 포인트: 침해된 WebContent process가 다른 origin에게 발급된 handle identifier를 재생(replay)하면, 해당 origin의 private filesystem을 network process의 storage 내부에서 그대로 열거·읽기·덮어쓰기·삭제할 수 있습니다.
NetworkStorageManager로 전송되는 다수의 FileSystem 관련 메시지는 FileSystem storage에 대한 operation을 요청할 때 FileSystemHandleIdentifier만 담아 보냅니다. 그런데 NetworkStorageManager는 발신 process가 실제로 해당 handle에 접근 권한이 있는지 확인하지 않았습니다. 이로 인해 침해된 process가 FileSystemHandleIdentifier를 위조해 다른 origin의 데이터에 접근할 수 있었습니다. 패치에서는 FileSystemStorageManager에 origin을 저장하고 FileSystemStorageHandle에 origin accessor를 추가함으로써, NetworkStorageManager가 FileSystem message handler들에서 isSiteAllowedForConnection을 실행할 수 있도록 했습니다. API test로는 IPCTestingAPI.FileSystemForgedHandleIdentifierRejected가 추가되었습니다.
Source/WebKit/NetworkProcess/storage/NetworkStorageManager.cpp
Source/WebKit/NetworkProcess/storage/FileSystemStorageHandle.cpp
Tools/TestWebKitAPI/Tests/WebKit/WKWebView/IPCTestingAPI.mm
Patch Details
이번 변경은 소유 origin을 FileSystem storage object graph 안으로 끌어들이고, 열두 개 handler의 signature를 넓혀 발신자를 확인할 수 있게 하며, 열여섯 곳의 call site에 authorization predicate를 하나씩 삽입합니다.
배관(plumbing) 쪽을 보면, FileSystemStorageManager에 WebCore::ClientOrigin m_origin 멤버가 추가되었고, 이는 create와 생성자에 새로 추가된 const WebCore::ClientOrigin& 파라미터로부터 설정되며 origin()으로 노출됩니다. OriginStorageManager::fileSystemStorageManager()와 OriginStorageManager::StorageBucket::fileSystemStorageManager()도 동일한 origin 파라미터를 갖게 되었고, NetworkStorageManager.cpp의 여섯 개 fileSystemStorageManager(...) 호출 지점(fileSystemGetDirectory, addGlobalIdentifierReference, removeGlobalIdentifierReferences, resolveGlobalIdentifier, registerFileSystemHandleRecordsForOrigin, putOrAdd IDB lambda) 모두가 이를 전달합니다. FileSystemStorageHandle::origin() const도 새로 추가되어, WeakPtr<FileSystemStorageManager> m_manager를 통해 m_manager->origin()을 반환하거나, manager가 이미 사라진 경우 std::nullopt를 반환합니다.
집행(enforcement) 쪽에서는 NetworkStorageManager에 private helper인 canConnectionAccessFileSystemHandle(IPC::Connection::UniqueID, const FileSystemStorageHandle&) const가 추가되었습니다. 이 helper는 handle의 origin을 확인한 뒤 기존의 isSiteAllowedForConnection(connection, WebCore::RegistrableDomain { origin->topOrigin })에 위임합니다. 이전에는 FileSystemHandleIdentifier만 받던 열두 개 message handler — closeHandle, isSameEntry, move, removeEntry, resolve, createSyncAccessHandle, closeSyncAccessHandle, requestNewCapacityForSyncAccessHandle, createWritable, closeWritable, executeCommandForWritable, getHandleNames — 는 .cpp와 .h 양쪽 모두에서 첫 번째 파라미터로 IPC::Connection&을 받도록 바뀌었습니다. 이 열두 개에 더해, 이미 connection을 받고 있었지만 검증이 없었던 네 개(getFileHandle, getDirectoryHandle, getFile, getHandle)까지 총 열여섯 개 모두에 registry의 null 체크 직후, handle을 실제로 다루기 전 위치에 MESSAGE_CHECK / MESSAGE_CHECK_COMPLETION이 새로 추가되었습니다. closeHandle과 closeSyncAccessHandle은 if (RefPtr handle = ...) 형태에서 조기 반환(early-return) 형태로 재구성되어, 이 체크를 삽입할 수 있게 되었습니다.
세 번째 축은 테스트입니다. IPCTestingAPI.FileSystemForgedHandleIdentifierRejected는 한 origin의 outgoing IPC에서 실제 FileSystemHandleIdentifier를 포착한 뒤, 이를 origin이 다른 WebContent process에서 재생합니다.
Authorization은 리소스 소유자의 identity를 함께 담아 보내는 요청에만 적용되었고, 같은 리소스를 opaque한 global handle로만 지칭하는 요청은 검증을 완전히 건너뛰었습니다.
Background
이 코드가 있는 위치.
NetworkStorageManager는 network process 내에서 PAL::SessionID마다 하나씩 인스턴스화되는 IPC::WorkQueueMessageReceiver로, FileSystem, Local/SessionStorage, IndexedDB, CacheStorage 등 WebContent process가 보내는 storage 메시지를 처리합니다. Origin별 OriginStorageManager 인스턴스를 소유하며, storage 수준의 origin isolation을 집행하는 지점에 해당합니다.
Origin Private File System.
OPFS는 navigator.storage.getDirectory()를 통해 web content에 노출되는 origin별 private filesystem으로, 페이지에 디렉터리 handle, 파일 handle, createWritable() 스트림, 그리고 byte 단위 I/O를 위한 createSyncAccessHandle()을 제공합니다. 실제 파일은 network process에 존재하며, WebContent는 이를 IPC 상에서 지칭하는 proxy만 갖습니다.
Identifier와 registry.
WebCore::FileSystemHandleIdentifier는 network process가 발급하는 ObjectIdentifier 형태의 opaque한 정수입니다(FileSystemStorageHandle은 Identified<WebCore::FileSystemHandleIdentifier>를 상속합니다). 이 값은 디렉터리나 파일 handle을 지칭하기 위해 WebContent에 반환됩니다. FileSystemStorageHandleRegistry는 NetworkStorageManager가 소유하는 테이블로, identifier를 live handle에 매핑합니다. Manager가 세션 단위이기 때문에, getHandle(identifier)는 해당 세션 내 모든 origin과 모든 connection의 handle을 대상으로 flat lookup을 수행합니다.
ClientOrigin과 RegistrableDomain.
WebCore::ClientOrigin은 두 origin의 쌍입니다. topOrigin은 storage partitioning에 쓰이는 top-level document의 origin이고, clientOrigin은 실제 접근을 수행하는 frame이나 worker의 origin입니다. Storage 디렉터리는 이 둘을 해시해 도출됩니다. WebCore::RegistrableDomain은 origin의 eTLD+1로, WebKit의 site isolation 관련 관리는 full origin이 아니라 site 단위로 이루어집니다.
isSiteAllowedForConnection.
NetworkStorageManager가 이미 갖고 있던 predicate로, "UI process가 이 WebContent connection이 해당 site를 대신해 동작하도록 허용했는가?"를 답합니다. Storage site validation이 활성화되어 있을 때 동작하며, API test에서는 _setStorageSiteValidationEnabled:YES로 이를 명시적으로 켭니다.
MESSAGE_CHECK / MESSAGE_CHECK_COMPLETION.
WebKit의 IPC validation 매크로입니다. 검증 조건이 거짓이면 해당 메시지를 오작동하는 발신자의 증거로 간주해 connection을 종료하거나 그에 준하는 페널티를 부과합니다. _COMPLETION 버전은 먼저 전달된 completion handler를 호출해, async reply가 유실되지 않도록 합니다.
IPC Testing API.
WKPreferences 단위로 활성화되는 debug 전용 WebKit 기능으로, 페이지 JavaScript가 outgoing IPC를 관찰하거나(IPC.addOutgoingMessageListener) 다른 process에 임의로 조립한 메시지를 보낼 수 있게(IPC.connectionForProcessTarget(...).sendWithAsyncReply) 해줍니다. 침해된 WebContent process 안의 native code가 할 수 있는 일을 시뮬레이션하는 용도입니다.
정상 흐름.
WebContent가 navigator.storage.getDirectory()를 호출하면 ClientOrigin을 담은 FileSystemGetDirectory 메시지가 전송됩니다. NetworkStorageManager는 connection에 대해 origin을 검증한 뒤, OriginStorageManager를 통해 해당 origin의 FileSystemStorageManager를 얻고, root FileSystemStorageHandle을 생성해 등록한 뒤 그 identifier를 반환합니다. 이후의 모든 operation은 이 identifier를 참조합니다.
Analysis
같은 파일 안에 두 종류의 authorization 체계가 나란히 존재했습니다. Payload에 ClientOrigin을 담고 있는 메시지들 — FileSystemGetDirectory, AddGlobalIdentifierReference, RemoveGlobalIdentifierReferences, ResolveGlobalIdentifier — 은 MESSAGE_CHECK(isSiteAllowedForConnection(connection.uniqueID(), RegistrableDomain { origin.topOrigin }), connection)으로 보호되었습니다. 반면 registry를 통해 대상을 찾아내는 열여섯 개 handler에는 대응하는 체크가 없었고, 그중 열두 개는 체크를 작성하는 데 필요한 IPC::Connection&조차 받고 있지 않았습니다.
WebContent A Network process (per SessionID) WebContent B
──────────── ─────────────────────────────── ────────────
getDirectory(origin A) ──► [origin check] ──► handle #42
FileSystemStorageHandleRegistry
{ 42 -> handle(origin A) }
▲
│ flat lookup, no owner check
└───────────────────────── GetHandleNames(42)
(no ClientOrigin
in payload)
근본 원인은 handle 객체가 "나는 누구의 소유인가"라는 질문에 답할 수 없었다는 점입니다. FileSystemStorageHandle은 m_path, m_name, m_type과 WeakPtr<FileSystemStorageManager> m_manager를 저장하고 있었고, FileSystemStorageManager는 디스크 경로는 저장했지만 자신이 생성된 ClientOrigin은 저장하지 않았습니다. Authorization을 수행하려는 handler가 있었다 하더라도, 대조할 대상 자체가 없었던 셈입니다.
이 문제는 memory-safety bug가 아닙니다. getHandle()은 알 수 없는 identifier에 대해 nullptr을 반환하고 모든 handler가 이미 null 체크를 수행하고 있었으므로, 위조된 identifier는 존재하지 않는 handle로 귀결되거나, 아니면 다른 origin에 속한 유효하고 타입도 올바른 handle로 귀결됩니다. Network process 입장에서는 이 identifier를 위조 불가능한 capability로 취급했지만, IPC 경계를 넘는 순간 발신자가 완전히 제어할 수 있는 평범한 정수로 취급되었습니다.
이 API test는 실제로 동작하는 proof of concept이며, end to end로 추적됩니다. https://good.example/가 하나의 WebContent process에서 로드되어 await navigator.storage.getDirectory()를 실행한 뒤 root.getFileHandle('test.txt', { create: true })를 호출합니다. Network process는 root 디렉터리 handle에 대한 identifier를 발급해 등록합니다. 이후 페이지가 root.entries()를 순회하면서 NetworkStorageManager_GetHandleNames 메시지가 발생하는데, 설치된 outgoing-message listener가 테스트에 하드코딩된 오프셋(buf.getBigUint64(16, true))에서 identifier를 읽어내 alert('id:' + ...)로 노출시킵니다. 그 다음 https://bad.example/가 다른 WebContent process에서 로드됩니다. 테스트는 EXPECT_NE(goodPID, badPID)로 이를 확인하는데, 두 process는 network process와 세션 범위의 registry 외에는 아무것도 공유하지 않습니다. 이 상태에서 IPC.connectionForProcessTarget('Networking').sendWithAsyncReply(0, NetworkStorageManager_GetHandleNames, [{ type: 'uint64_t', value: BigInt(stolenId) }], onReply)가 호출됩니다.
패치 이전에는 getHandleNames가 이 정수를 flat registry에서 조회해 good.example의 live root 디렉터리 handle을 찾아낸 뒤, 조건 없이 handle->getHandleNames()를 호출했습니다. 따라서 reply에는 다른 origin의 디렉터리 목록이 실려 반환되었을 것입니다. 테스트는 자체 decoder가 가정하는 오프셋에서 reply의 성공 flag(buf.getUint8(16))를 읽어 성공 여부를 판단하며, 이 경우 FAIL:access-granted가 보고됩니다. 패치 이후에는 canConnectionAccessFileSystemHandle이 FileSystemStorageManager::m_origin을 통해 handle의 ClientOrigin을 확인하고, isSiteAllowedForConnection이 bad.example의 connection에 대해 false를 반환합니다. 그 결과 MESSAGE_CHECK_COMPLETION이 FileSystemStorageError::Unknown(PASS:access-denied)으로 응답하는 동시에 발신자를 문제 있는 것으로 표시합니다.
실제 공격 상황에서는 IPC Testing API가 침해된 WebContent process 안의 native code를 대신합니다. Identifier가 열거 가능한 형태로 밝혀진다면 포착(capture) 단계 자체가 불필요해질 수 있습니다. 작은 정수 범위에 대해 반복적으로 GetHandleNames를 호출하는 것만으로 oracle 역할을 할 수 있기 때문입니다. 다만 identifier 공간이 실제로 그 정도로 작거나 순차적인지는 발급기의 값 분포에 달려 있고, 이번 변경은 이를 규명하지 않습니다. 따라서 이번 증거가 보여주는 경로는 포착 후 재생(capture-and-replay)뿐입니다. 정보 노출을 넘어서는 확장은 동일한 방식의 재생을 write-side handler에 적용하는 경로를 통해 가능합니다. CreateWritable과 ExecuteCommandForWritable을 조합하면 피해 origin의 OPFS 파일을 덮어쓸 수 있고, RemoveEntry/Move를 이용하면 삭제나 이동이 가능합니다. 이 handler들 각각에 이번 패치에서 동일한 MESSAGE_CHECK_COMPLETION이 추가되었다는 사실 자체가, 이들이 이전까지 무방비 상태였다는 근거입니다. 읽기·쓰기를 넘어, CloseHandle/CloseSyncAccessHandle/CloseWritable은 다른 origin의 live handle에 대한 cross-origin denial of service로 이어질 수 있고, RequestNewCapacityForSyncAccessHandle은 quota 조작으로 이어질 수 있습니다.
공격자의 위치는 WebContent process이고, 취약한 코드는 network process의 NetworkStorageManager work queue 위에서 동작합니다. 이는 sandbox escape가 아닙니다. Network process 안에서 새로운 code execution을 얻는 것도 아닙니다. 이 취약점이 무너뜨리는 것은 network process가 맡고 있는 "cross-origin storage isolation의 중재자" 역할입니다. 그 결과 하나의 사이트에 국한된 WebContent 침해가 같은 세션 내 모든 사이트의 OPFS 데이터에 도달할 수 있게 됩니다. Registry가 SessionID 단위이므로 세션을 넘어선 데이터는 이 경로로는 닿지 않습니다. 또한 새로 추가된 체크가 isSiteAllowedForConnection에 위임하는 구조이기 때문에, 그 효과는 storage site validation이 활성화되어 있는지에 달려 있습니다. API test는 _setStorageSiteValidationEnabled:YES를 명시적으로 설정하며, 이 모드가 꺼져 있을 때의 동작은 이번 패치가 다루지 않는 별개의 질문으로 남습니다.
이 취약점은 network process가 모든 WebContent process를 대신해 집행해야 할 cross-origin storage isolation 경계를 약화시킵니다. Security model은 FileSystemHandleIdentifier가 위조 불가능한 capability로 동작한다는 것, 즉 그것을 보유한다는 사실 자체가 발신자가 자신의 origin을 위해 그 handle을 부여받았음을 의미한다는 전제와, origin isolation이 신뢰받지 않는 발신자가 아니라 신뢰받는 network process에서 집행된다는 전제 위에 서 있습니다. 패치 이전에는 이 두 전제가 우연히 ClientOrigin을 함께 담아 보내는 메시지 부분집합에 대해서만 성립했습니다. WebContent process에 대한 code execution, 혹은 script 수준의 IPC primitive만 확보한 공격자라면 같은 세션 내 다른 origin의 OPFS 콘텐츠를 열거·읽기·덮어쓰기·이동·삭제할 수 있었습니다. 추가적인 network process memory corruption 없이도 도달 가능한, 디스크에 영속되는 형태의 cross-origin 데이터 노출 및 변조 primitive에 해당합니다.
Insight
구조적으로 흥미로운 지점은 이 공백이 왜 살아남았는가입니다. NetworkStorageManager는 이미 올바른 predicate를 갖고 있었고, 이를 일관되게 적용하고도 있었습니다. 다만 payload에 ClientOrigin이 우연히 포함되어 있는 메시지에만 적용되었다는 점이 문제입니다. 체크가 보안상 민감한 operation이 아니라 wire format 상 origin의 존재 여부에 묶여 있었던 셈이고, 그 결과 origin이 사실상 불필요했던 메시지들은 검증되고 origin 검증이 실제로 load-bearing했던 메시지들은 검증에서 빠지는 정반대의 결과가 나왔습니다. 이 문제가 성립할 수 있었던 조건은 handle이 자신의 origin을 가리키는 back-pointer를 애초에 갖고 있지 않았다는 데 있습니다. 이번 패치의 실질적인 부분은 배관(plumbing) 작업이며, 일단 그 배관이 갖춰지고 나면 열여섯 줄의 MESSAGE_CHECK는 기계적으로 따라옵니다. 하나의 세션 안에서 모든 origin과 connection이 공유하는, 단순 identifier로 키가 매겨진 registry는 이런 종류의 버그를 구조적으로 초대하는 설계입니다. Connection 단위로 partition된 registry였거나, connection마다 별도로 identifier를 발급하는 구조였다면, 열여섯 곳에 반복적으로 체크를 심는 대신 이 메시지 계열 전체가 설계 자체로 안전했을 것입니다.
Audit directions
-
권한 검증이 수행되는 작업 자체가 아니라, payload에 identity 필드가 존재하는지 여부에 연동된 방식 — 그 결과 리소스를 opaque handle로 지칭하는 요청은 형제 요청이 받는 검증을 조용히 건너뛰게 됩니다. Narrow:
NetworkStorageManager.messages.in에 남아 있는 나머지NetworkStorageManager메시지 계열 중,ClientOrigin없이 단순 identifier만 받는 것들을 점검해야 합니다 —DisconnectFromStorageArea/SetItem/RemoveItem/Clear(WebKit::StorageAreaIdentifier),CacheStorageRemoveCache/CacheStorageReference/CacheStorageRetrieveRecords/CacheStoragePutRecords(WebCore::DOMCacheIdentifier), 그리고IDBDatabaseConnectionIdentifier/IDBResourceIdentifier를 키로 사용하는 IDB 핸들러들입니다. 각 핸들러가 identifier를 세션 내 모든 origin에 걸쳐 있는 registry를 통해 resolve하는지 확인할 필요가 있습니다. Match tell: 어떤 핸들러가someRegistry->get*(identifier)를 호출한 직후 바로 사용하면서, 그 사이에isSiteAllowedForConnection/canConnectionAccessSiteForWebStorage같은 검증이 없는 경우입니다. 반면 같은 파일 안의 인접 핸들러에는 이런 검증이 존재한다면, 그 차이가 바로 단서가 됩니다. Wider: 동일한 패턴이 privileged process가 정수 handle을 발급한 뒤 이를 그대로 신뢰해 되돌려 받는 모든 곳에서 나타날 수 있습니다 — GPU process의RemoteRenderingBackend/RemoteGraphicsContextGLobject identifier,WebSWServerConnection의 service-worker registration identifier,RemoteMediaPlayerManagerProxy의 player identifier 등이 해당됩니다. 여러 connection에서 도달 가능한 공유HashMap<SomeIdentifier, Ref<T>>가 있는지 살펴봐야 합니다. Widest: 이 문제의 본질은 handle이 capability로 성립하려면 unguessable해야 할 뿐 아니라 보유자에게 scope되어야 한다는 것 — 그렇지 않으면 모든 핸들러가 각자 owner를 재도출하고 authorize해야 한다는 invariant입니다. 이는 raw ID를 per-interface message pipe 대신 전달하는 Chromium Mojo interface, path 기반 API와 대비되는 POSIX file descriptor, 그리고 순차적 object ID를 사용하는 REST API(고전적인 IDOR/BOLA 계열)에도 동일하게 적용됩니다. 어떤 codebase에서든 match tell은 신뢰된 쪽이 발급한 identifier가 신뢰되지 않는 쪽으로 전달된 뒤, 나중에 server-side ownership lookup 없이 entitlement의 증명으로 그대로 받아들여지는 패턴입니다. -
"나를 소유한 것이 누구인가"에 답할 수 없는 object — back-reference의 부재야말로 authorization check를 작성 불가능하게 만드는 근본 원인이며, 따라서 check의 부재는 증상일 뿐 질병 자체는 아닙니다. WebKit의 network-process 및 GPU-process 리소스 클래스 중, 공유 registry에 등록되면서도 origin, session, connection provenance를 전혀 저장하지 않는 타입들을 점검해야 합니다. Narrow:
Source/WebKit/NetworkProcess/storage내부의StorageAreaBase서브클래스와CacheStorageCache에서, origin 또는ClientOrigin이 object 자체로부터 도달 가능한지(현재의FileSystemStorageManager::m_origin처럼), 아니면 그것을 생성한 manager로부터만 도달 가능한지 확인해야 합니다. Wider: 동일한 형태가 factory는 security context를 알지만 그 결과물은 이를 모르는 모든 곳에서 반복됩니다 — GPU-process의RemoteResourceCacheentry,RemoteAudioDestinationManager의 destination, ServiceWorker의SWServerWorkerrecord 등입니다. 단서는 생성자가 path, buffer, handle은 받지만 그것이 생성된 대상 principal은 받지 않는 클래스입니다. Widest: 보안과 관련된 모든 object는 자신의 principal을 스스로 밝힐 수 있어야 하는데, owner를 찾기 위해 별도 table을 조회해야 하는 access-control 코드는 결국 언젠가는 생략되기 때문입니다. 이는 request handler가 primary key로 row를 조회하는 모든 multi-tenant server, namespace를 넘나들며 object를 reconcile하는 Kubernetes 스타일 controller, 그리고 realm/compartment tag를 object에 부여하는 언어 런타임에도 적용됩니다. Match tell: subject와 resource를 모두 인자로 받으면서도 이 둘을 연결하기 위해 제3의 구조를 조회해야 하는 authorization 함수입니다. -
새로 추가된 guard가 패치가 exercise하지 않는 조건에서도 실제로 유효한지 검증할 필요가 있습니다.
canConnectionAccessFileSystemHandle은handle.origin()이std::nullopt를 반환할 때 false를 반환하는데, 이는WeakPtr<FileSystemStorageManager> m_manager가 clear된 경우에 발생합니다. Handle이FileSystemStorageHandleRegistry에 등록된 상태를 유지하면서도 manager보다 오래 살아남을 수 있는지 추적할 필요가 있습니다. 그 window에서는 해당 handle에 대한 모든 operation이 이제 fail closed 상태가 되기 때문입니다(동작 자체는 올바르지만,FileSystemStorageManager::~FileSystemStorageManager→close()와 대조해 확인해볼 가치가 있는 behavior change입니다). 별도로, 이 check는ClientOrigin을RegistrableDomain { origin->topOrigin }으로 축소하면서clientOrigin을 버리고 full origin을 eTLD+1 수준으로 좁힙니다. 같은 top-level registrable domain을 공유하지만clientOrigin이나 scheme/port가 다른 두 partition이 서로의 handle에 여전히 접근할 수 있는지, 그리고 storage site validation이 비활성화된 상태에서 이 predicate가 permissive하게 동작하는지 점검해야 합니다. Match tell: storage directory를 도출하는 데 사용되는 key보다 더 coarse한 granularity로 비교하는MESSAGE_CHECK입니다 — 여기서는 origin directory path가topOrigin과clientOrigin을 모두 hash하는 반면, check는topOrigin의 registrable domain만 검사합니다. -
call-site마다 반복되는 enforcement, 즉 사람이 새 handler를 작성할 때마다 guard를 빠짐없이 붙여넣어야 보안이 유지되는 구조입니다. 이 패치는 동일한
MESSAGE_CHECK_COMPLETION(canConnectionAccessFileSystemHandle(...))라인을 열여섯 번 추가하는데, 내년에 추가될 열일곱 번째 handler가 바로 다음 버그가 될 가능성이 있습니다.Source/WebKit/Scripts/webkit/messages.py의 IPC message 생성 machinery와.messages.in의 attribute vocabulary(EnabledBy,DispatchedFrom,SharedPreferencesNeedsConnection)가 ownership-validation attribute를 표현할 수 있는지, 그래서 authorization이 손으로 작성되는 대신 생성될 수 있는지 조사할 필요가 있습니다. 또한 기존 receiver들 중 origin scoping을 순전히 수작업MESSAGE_CHECK에만 의존하는 것들이 어떤 것인지 점검해야 합니다. Wider: 동일한 취약성이 WebKit의 sandbox extension 처리와, resource identifier를 받는NetworkConnectionToWebProcesshandler에서도 나타납니다. Widest: N개의 call site에 반복되는 convention으로 구현된 authorization은 N에 비례하는 실패율을 가지지만, type이나 wrapper, 혹은 generated preamble로 구현된 authorization은 실패율이 하나로 고정된다는 것이 본질입니다. 이는 Rust의 newtype-wrapped authorized ID, Mojo의 typed capability token, 그리고 web framework에서의 middleware 방식 인증 대 per-endpoint 인증에도 동일하게 적용됩니다. Match tell: 동일한 guard 표현식이 verbatim으로 5회 이상 반복되는 handler family를 grep한 뒤, 이 guard를 가진 handler 집합과 동일한 identifier type을 resolve하는 handler 집합을 diff해보면, 그 차이가 곧 버그 목록이 됩니다.