[2] HTMLResourcePreloader CSP bypass via empty-nonce preloading
Severity: High | Component: WebCore HTML parser — HTMLResourcePreloader | 88d6ffd
meta CSP 태그 이후에 로드되는 리소스에 대한 완전한 Content Security Policy bypass가 관찰 가능한 영향이며, meta 태그 주변의 HTML 구조를 제어함으로써 web content에서 도달 가능하기 때문에 High로 평가됩니다. 비어 있는 policy set에 대한 empty nonce 평가 문제는 fix 패턴을 통해 confidence 0.92로 확인됩니다.
Parser-blocking script이 meta CSP 태그보다 앞에 선언되어 있으면, preload scanner는 CSP policy가 파싱되기 전에 그 이후의 리소스를 먼저 fetch합니다. HTMLResourcePreloader는 각 preload 대상 script/stylesheet에 대해 empty nonce를 인자로 allowScriptWithNonce/allowStyleWithNonce를 호출합니다. 이 시점에는 아직 policy가 존재하지 않으므로 해당 함수들은 true를 반환합니다. HTMLResourcePreloader는 이 true를 유효한 nonce 일치로 해석하여 리소스의 ResourceLoaderOptions에 ContentSecurityPolicyImposition::SkipPolicyCheck를 설정합니다. 이 플래그는 리소스의 생애 주기 전반에 걸쳐 SubresourceLoader에 남아 있으며, 결과적으로 CSP policy를 위반하는 redirect 대상도 차단 없이 로드됩니다.
Source/WebCore/html/parser/HTMLResourcePreloader.cpp
bool skipContentSecurityPolicyCheck = false;
- if (m_resourceType == CachedResource::Type::Script || m_resourceType == CachedResource::Type::JSON)
- skipContentSecurityPolicyCheck = protect(document.contentSecurityPolicy())->allowScriptWithNonce(m_nonceAttribute);
- else if (m_resourceType == CachedResource::Type::CSSStyleSheet)
- skipContentSecurityPolicyCheck = protect(document.contentSecurityPolicy())->allowStyleWithNonce(m_nonceAttribute);
+ if (!m_nonceAttribute.isEmpty()) {
+ if (m_resourceType == CachedResource::Type::Script || m_resourceType == CachedResource::Type::JSON)
+ skipContentSecurityPolicyCheck = protect(document.contentSecurityPolicy())->allowScriptWithNonce(m_nonceAttribute);
+ else if (m_resourceType == CachedResource::Type::CSSStyleSheet)
+ skipContentSecurityPolicyCheck = protect(document.contentSecurityPolicy())->allowStyleWithNonce(m_nonceAttribute);
+ }
Patch Details
PreloadRequest::resourceRequest()에 m_nonceAttribute.isEmpty() 조건을 추가하여, allowScriptWithNonce / allowStyleWithNonce가 실제로 nonce를 포함한 preload 요청에서만 호출되도록 수정되었습니다. 패치 이전에는 nonce 존재 여부와 무관하게 empty nonce string으로 이 함수들이 호출되었습니다. meta CSP 태그가 아직 파싱되지 않아 policy가 없는 상태에서는 true가 반환되었고, 이로 인해 리소스의 ResourceLoaderOptions에 SkipPolicyCheck가 설정되었습니다. ios-site-isolation 및 mac-site-isolation의 테스트 기대값 파일에서는 이 버그로 인해 timeout이 발생하던 script-redirect-blocked.html과 stylesheet-redirect-blocked.html의 [Timeout] 마커가 제거되었습니다.
빈 policy set에 대한 speculative preloading 중 CSP nonce가 조기에 평가되어, resource loader에 영구적인 policy check bypass 플래그가 설정되는 문제.
Background
Content Security Policy (CSP)는 HTTP 헤더 외에도 <meta http-equiv="Content-Security-Policy"> 태그를 통해 적용할 수 있습니다. 단, meta 태그 방식은 HTML parser가 해당 태그를 실제로 처리하는 시점에 policy가 활성화됩니다. WebKit의 preload scanner(HTMLResourcePreloader)는 메인 HTML parser보다 앞서 실행되어 subresource(script, stylesheet, image 등)를 미리 발견하고 투기적으로 fetch합니다. 페이지 로딩 속도를 높이기 위한 성능 최적화이지만, security policy가 설치되기 전에 리소스가 fetch될 수 있는 timing window가 만들어진다는 부작용이 있습니다.
CSP nonce 기반 allowlist는 리소스가 policy에 선언된 nonce와 일치하는 nonce 속성을 가질 경우 CSP 검사를 우회할 수 있게 합니다. allowScriptWithNonce()와 allowStyleWithNonce()는 nonce가 policy의 allowlist와 일치하거나, policy 자체가 존재하지 않을 때(vacuously true) true를 반환합니다. ContentSecurityPolicyImposition::SkipPolicyCheck는 ResourceLoaderOptions의 플래그로, 해당 리소스에 대한 모든 CSP 검사를 건너뛰도록 지시합니다. redirect 대상에 대한 검사도 예외가 없습니다. 이 플래그는 한 번 설정되면 리소스의 생애 주기 전반에 걸쳐 SubresourceLoader에 유지됩니다.
Analysis
root cause는 allowScriptWithNonce("")의 반환값 의미를 잘못 해석한 데 있습니다. 패치 이전에는 PreloadRequest::resourceRequest()가 m_nonceAttribute가 비어 있는 경우에도 무조건 allowScriptWithNonce(m_nonceAttribute)를 호출했습니다.
preload scanner는 HTML parser보다 앞서 실행됩니다. <meta http-equiv="Content-Security-Policy"> 태그보다 앞에 parser-blocking script가 있으면, preload scanner는 CSP policy가 파싱되기 전에 meta 태그 이후에 선언된 리소스를 먼저 fetch합니다. 이 시점에서 document.contentSecurityPolicy()에는 아무 policy도 없습니다. 빈 policy set에 대해 allowScriptWithNonce("")를 호출하면 true가 반환됩니다. 거부할 policy가 존재하지 않기 때문입니다(fix 패턴이 강하게 시사하는 동작 방식을 가정할 때).
HTMLResourcePreloader는 이 true를 유효한 nonce 일치로 해석하여 SkipPolicyCheck를 설정합니다. 이 플래그는 redirect 전반에 걸쳐 SubresourceLoader에 유지됩니다(script-redirect-blocked, stylesheet-redirect-blocked 테스트명이 이를 보여줍니다). 이후 meta CSP 태그가 파싱되고 리소스가 CSP를 위반하는 URL로 redirect되더라도, redirect 검사가 건너뛰어집니다.
Aaaaaa Aaaaa Aa Aaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aa Aaaa Aa Aaa Aaaa Aaaaa
Aaaaaaa
Aaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
Aaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
Aaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
Aaaaaaaaaaaaaaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaaaaaaaaaa
Aaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
Aaa
Aaa Aaaaaaa Aaaa Aaa Aa Aaa Aaaa Aaaa Aaaaaaaa Aaaaaa Aaaa Aaaa Aa Aaaaaaaaaaaa Aaaaa Aaaaa Aaaa Aaa Aaaaaaa Aaaaaaaa Aaaa Aaa Aaaa Aa Aa Aaaaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaa Aaaaaaa Aaaaaa Aaaa Aaaaaaaaaaaaaaaaaa Aaa Aa Aaaaaaaaaaaaa Aaaaaaaaa Aa Aa Aaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaa Aaaa Aaaa Aaa Aaaa Aaaaaaaaaaaaaaa Aaaa Aaaaaaaaaaa
a Aaaa Aaa Aaaaaaaaa Aaa Aaaaaaa Aa Aaaaaaa Aaaaaa Aaaa Aaaaaaaaaaa Aaa Aaaa Aa Aaaa Aa Aaa Aaaa Aaa a Aa Aaaaa Aaaa Aaa Aa Aaa Aaaa Aaa Aaa a Aaaaa Aa Aaaaa Aaaaaaa Aaaaa Aaa Aaaaaaa Aaaaaaa Aaa a Aaaaa Aaaa Aaaa Aaa Aa Aa Aaaaaaaaaaaaaaa Aaaaaaa Aaaaa a Aa Aaaa Aaaa Aaaaaaaa Aaaaaaaaa Aaaaaa Aaaa Aaaaaaa Aa Aaaa Aaaa Aaaaaa Aaaaaaaa Aaaaa Aaa Aaa Aa Aaaaaaaa Aaa Aa Aa Aaaaaa
a Aaaa Aaaaaaa Aaaaaaaa Aaaaaaa Aa Aaa Aaaaaaa Aaaa Aa Aaa Aaaaaaaa Aaa Aaa Aa Aaaaaaaaaaa Aaa Aaaa Aaaaaaa Aa Aaaaa Aaaaa Aa Aaaa Aaa Aaa Aaa a Aa Aaaa Aaaaaaa Aaaaaaaaaaa Aaaaaaaaaaaaaaaaaa Aa Aaaaa Aaaaaaaaaa Aaa Aa Aaaa Aaaa a Aaa Aaaaaaaa Aaaaaaaaaa a Aaaaaa Aaaaa Aaaa Aa Aaaa Aaa Aaaa Aaaaaaaa Aaaa Aa Aaa Aaaaa Aaa Aaaa Aaaaa Aaa Aaa Aaa Aaaaaa Aaa Aaaaaaa
a Aaaa Aa Aaa Aaaaaaaaaaaaaaaaaa Aa Aaaaaaa Aaaa Aa a Aa Aaa Aaa Aaaa Aaaaaaa Aaa Aaaa Aa Aaaaa Aaaaa Aa Aaaa Aaaa Aaaaaa Aaa Aaaa Aaaaaaaaaaaaaaaaaa Aaaa Aa Aaaa Aaaaaaa Aaa Aaaa Aaa Aaaa Aaaaa a Aaa Aaaaaaaaaa Aaaa Aa Aaa Aaa Aaaaa Aaaaaa Aaaaa Aa Aaaaaa Aaa a Aa Aaaaaaa Aa Aaaa Aaa Aaaa Aaaa Aaaaaaaaaaa Aaaaaaaaaa a Aaaaaaaaaaaaaaaaa Aaaa Aa Aaaa Aaa Aaaaa Aaaa Aaaaa
Aaaaaaaaa Aa Aa Aaaaa Aa Aaaaaaaaaaaaaaaaaaaaaaaaaaa Aaa Aaaaaaa Aa a Aaaaaaa Aaaaa Aa Aaaaaaaaaaaaaaaaaa Aaaaaaaa Aaa Aa Aaaaa Aa Aaa Aaa Aaa Aaaaa Aaa Aaaaa Aa Aaaaaa Aaaaa Aaa Aaaa Aaaaaaa
🔒Explores how the speculative preloader's interaction with CSP timing creates a permanent policy bypass, and the conditions under which this is exploitable
더 확인하려면 구독해 주세요
Audit directions
a Aaaaaaaa Aa Aaaa Aaaa Aaaaaaaa Aaaaaaa Aa Aaaa Aaaa Aa Aaaaaaaa Aaaaaa Aaa Aaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaa Aa Aaaa Aaaaa Aaaa Aaaa Aaaaaaa Aaaa Aaaaaaaaaaaaaaaaaa Aaaaa a Aa Aaa Aa Aaa Aa Aaaaa Aaa Aaaaaaa Aaaa Aaaa Aaaaa Aaaaaa Aaaaaaa Aaaaaaaa a a Aaaaa Aaaa Aaaaaaaa Aaaaaaaaaaaaaa Aaaaaaaaaa Aaa Aa Aa Aaa Aaa Aaa Aaa Aaaaa
a Aaaaaaaaa Aa a Aaaaaaaa Aaaaa Aaaa Aaaaaaaaaaaaa Aaaa Aa Aaaaa Aaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaaaaaaaaaaaaaaa Aaaaaa Aaaa Aa Aa a Aaaaaaa Aaaaaa Aaaa Aaaaaaaaa Aaaa Aaa Aaaaaa Aaaa a Aaaaaaa Aaaaaa Aaaaaaa Aaa Aaaa Aaaaaa Aa Aaa Aaaaa Aaaaaaaaaaaaaaaaaa Aaaaaaaaaaaa Aaaaaaaaaaaaaaaaaaa Aaaaaaa Aaaaaaa Aaaa Aaaaaaaa Aaaaaaaaaa Aaaa Aa Aaa Aaa Aaaa Aaaa Aa Aaaaaaa Aa Aaa Aaaa Aaa a Aa Aaaaaaaa Aaaaaaaaa Aaaaa Aa Aaa Aaaaa Aaaa Aaaa
a Aaaaaa Aa Aa Aaaa Aaaaaa Aaaaaa Aaaaa Aa Aaaaaaaaa Aaaaaaa Aaaaaaa Aaaaaaaa Aaaa Aa Aaa Aaa Aaaa Aa Aaa a Aaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aa Aaaaaaaaaaaaaaaaaaa Aaa Aaa Aa Aaa Aaaa Aaa Aa a Aaaa Aaa Aaaaa Aaa Aa Aa Aaaaaaa Aaa Aaaaaaaaaaaaaaaaaa Aaa Aaaa Aaa Aaaa Aaaa
🔒Multiple audit patterns identified around early security-flag decisions and vacuous policy checks, applicable across WebKit's resource loading infrastructure
더 확인하려면 구독해 주세요