← All issues

[3] HTMLPlugInElement skips content extensions for embed/object loads

Severity: Medium | Component: WebCore HTML plugin elements | 7d28e39

<embed>/<object> 마크업을 사용할 수 있는 모든 페이지에서 항상 재현 가능한 content blocker bypass를 수정하는 패치입니다. 영향 범위는 memory corruption이 아닌 policy 우회(사용자 설치 규칙을 벗어나는 tracker/malware/광고 로드)이므로 Medium으로 평가합니다.

<embed>, <object> 요소를 통해 로드되는 plugin content가 content extension 규칙 검사를 전혀 거치지 않았습니다. 이번 수정에서는 HTMLPlugInElement::canLoadURL에 content extension 검사가 추가되어, updateWidgetwouldLoadAsPlugIn 지연 처리 이전에 차단 대상 URL을 거부하도록 변경되었습니다.

Source/WebCore/html/HTMLPlugInElement.cpp

+#if ENABLE(CONTENT_EXTENSIONS)
+#include "ContentExtensionsBackend.h"
+#include "ResourceLoadInfo.h"
+#include "UserContentProvider.h"
+#endif
...
bool HTMLPlugInElement::canLoadURL(const URL& completeURL) const
{
+#if ENABLE(CONTENT_EXTENSIONS)
+ if (completeURL.isValid()) {
+ RefPtr page = document().page();
+ RefPtr frame = document().frame();
+ RefPtr documentLoader = frame ? frame->loader().documentLoader() : nullptr;
+ RefPtr userContentProvider = frame ? frame->userContentProvider() : nullptr;
+ if (page && documentLoader && userContentProvider) {
+ auto results = userContentProvider->processContentRuleListsForLoad(*page, completeURL, ContentExtensions::ResourceType::Other, *documentLoader);
+ if (results.shouldBlock())
+ return false;
+ }
+ }
+#endif
return !isProhibitedSelfReference(completeURL);
}

LayoutTests/http/tests/contentextensions/block-embed-element.html

+<embed src="http://127.0.0.1:8000/contentextensions/resources/should-load.html?should-be-blocked" type="text/html" width="100" height="100"></embed>
+<embed src="http://127.0.0.1:8000/media/resources/test.pdf?should-be-blocked" type="application/pdf" width="100" height="100"></embed>

ENABLE(CONTENT_EXTENSIONS)가 활성화된 상태에서 URL이 유효한 경우, canLoadURL은 document의 Page, DocumentLoader, UserContentProvider를 조회합니다. 이후 UserContentProvider::processContentRuleListsForLoad(page, completeURL, ContentExtensions::ResourceType::Other, documentLoader)를 호출하며, 반환된 결과의 shouldBlock()이 true이면 canLoadURL이 false를 반환하여 호출 측에서 plugin 리소스 로드를 건너뜁니다. 회귀 테스트는 .*should-be-blocked URL 필터에 매칭되는 <embed>, <object> 리소스를 로드하여, 표준 'Content blocker prevented frame...' 콘솔 메시지가 발생하는지 확인합니다.

리소스 로드 경로에서 content policy 검사가 누락되어, plugin element 클래스가 다른 element 유형에 균일하게 적용되는 필터를 우회할 수 있는 패턴.

Content Extensions(content blocker라고도 불림)는 WebKit 확장 기능 및 관리 구성에 선언형 규칙 목록 API를 제공합니다. 각 규칙은 trigger(URL 패턴, 리소스 유형, 로드 컨텍스트)와 action(block, block-cookies, css-display-none, make-https)의 쌍으로 이루어집니다. 로드 발생 시 UserContentProvider::processContentRuleListsForLoad가 모든 활성 규칙 목록을 평가하고 집계된 actions를 반환하는데, 호출 측은 results.shouldBlock()을 확인해 로드를 중단할지 결정합니다. HTMLPlugInElement<embed>, <object>, 레거시 <applet>의 C++ 베이스 클래스로, canLoadURL은 두 서브클래스 모두가 updateWidget 진입 전에 반드시 거치는 단일 진입점입니다. SubframeLoader에 plugin 또는 subframe widget 생성을 요청하기 이전에 이 함수를 통과하는 셈입니다. 한편 <iframe>, <img>, <script>, XHR/fetch 로드에 대한 content extension 훅은 각각의 loader 경로에 별도로 구현되어 있습니다.

HTMLPlugInElement::canLoadURL은 URL에 대해 self-reference 검사만 수행했을 뿐, content extension 엔진은 전혀 거치지 않았습니다. <img>, <script>, <iframe>, XHR 등 다른 리소스 유형에는 이미 규칙 목록 평가 경로가 존재했지만, plugin element 클래스에는 해당 경로가 연결되지 않은 상태였습니다.

🔒

How content-policy enforcement is distributed across WebKit's resource-loading classes, and what this commit reveals about coverage gaps in user-installed filters

더 확인하려면 구독해 주세요

🔒

Several reusable audit patterns identified for finding sibling bypasses in other element and loader classes, with concrete starting points

더 확인하려면 구독해 주세요