CSP wasm-unsafe-eval directive is not enforced during WebAssembly byte compilation
CSP의 wasm-unsafe-eval은 browsing context에서 WebAssembly를 컴파일하고 실행할 수 있는지를 제어합니다. WebKit은 이를 globalObject->webAssemblyEnabled() 호출로 구현했습니다. 문제는 이 호출이 JSWebAssemblyInstance::tryCreate() (instantiation) 단계에만 존재했고, 컴파일 단계에는 없었다는 점입니다.
WebAssembly.Module은 스펙상 structured-cloneable입니다. 공격자는 check를 우회하는 context에서 컴파일한 뒤, Module을 same-origin Worker에 postMessage로 전달할 수 있습니다. 이후 해당 Worker에서 instantiate하면 정책 전체를 무력화할 수 있었습니다.
이번 수정에서 check는 WebAssembly.compile(), new WebAssembly.Module(), WebAssembly.compileStreaming(), WebAssembly.instantiateStreaming() 각각에 추가되었습니다. bytecode 파싱이나 network fetch가 시작되기 전에 CompileError로 거부됩니다. 오랫동안 열려 있던 FIXME 두 건(bug 173977, 173105)도 함께 제거되었습니다.
Significance
streaming API 도입 이후 잠재해 있던 실질적인 CSP bypass를 해소했습니다. wasm-unsafe-eval이 이제 instantiation뿐만 아니라 Module 생성 자체를 차단합니다.
Audit directions
globalObject->webAssemblyEnabled()는 호출 시점의 global object를 기준으로 check됩니다. Dedicated, Shared, Service, Worklet 등 모든 Worker global이 각각 독립적으로 CSP context를 적용하는지 점검해야 합니다. 허용적인 parent context를 상속받는 경우가 없는지도 확인이 필요합니다. streaming variant는 fetch 시작 전에 CSP를 점검합니다. 다만 redirect를 통해 전달된 fetch response는 자체 헤더를 가집니다. redirect가 발생할 때 컴파일이 처음 점검한 것과 다른 CSP context로 이동할 가능성이 있는지 확인해야 합니다. WebAssembly.validate()는 설계상 check 대상에서 제외되어 있습니다. 향후 어떤 code path에서 validate() 결과를 활용해 컴파일을 단축하는 경우, 해당 경계는 살펴볼 필요가 있습니다. WebAssembly.instantiate()에 BufferSource를 직접 전달하는 경우 등 다른 진입점에도 동등한 guard가 추가되었는지 점검해야 합니다.