[9] UserMessageHandler.postMessage should fail if called from another frame
Severity: Medium | Component: WebCore page/UserMessageHandler | 795ef8a
Rated Medium because the observable effect is origin-spoofed delivery to a host-app WKScriptMessageHandler from a captured cross-origin reference, and impact depends on host-app trust placed on frameInfo/origin (no memory primitive).
Source/WebCore/page/UserMessageHandler.cpp
+static bool passesSameOriginCheck(JSC::JSGlobalObject& globalObject, RefPtr<LocalFrame> frame)
+{
+ if (!frame) return false;
+ RefPtr document = frame->document();
+ if (!document) return false;
+ Ref frameSecurityOrigin = document->securityOrigin();
+ if (!globalObject.inherits<JSDOMGlobalObject>()) return false;
+ RefPtr scriptExecutionContext = uncheckedDowncast<JSDOMGlobalObject>(globalObject).scriptExecutionContext();
+ if (!scriptExecutionContext) return false;
+ RefPtr securityOrigin = scriptExecutionContext->securityOrigin();
+ if (!securityOrigin) return false;
+ return securityOrigin->isSameOriginAs(frameSecurityOrigin);
+}
+
void UserMessageHandler::postMessage(JSC::JSGlobalObject& globalObject, JSC::JSValue value, Ref<DeferredPromise>&& promise)
{
RefPtr descriptor = m_descriptor;
- if (!descriptor) {
- promise->reject(Exception { ExceptionCode::InvalidAccessError });
- return Exception { ExceptionCode::InvalidAccessError };
- }
+ if (!descriptor)
+ return promise->reject(Exception { ExceptionCode::InvalidAccessError });
+ if (!passesSameOriginCheck(globalObject, m_frame.get()))
+ return promise->reject(Exception { ExceptionCode::InvalidAccessError, "Failed same-origin check."_s });
descriptor->didPostMessage(*this, globalObject, value, ...);
}
Patch Details
A new passesSameOriginCheck helper compares the calling JSGlobalObject's scriptExecutionContext security origin against the handler's owning LocalFrame's current document origin. Both postMessage and postLegacySynchronousMessage invoke it before forwarding to the descriptor. Return types are reshaped: postMessage is now void (rejecting via the promise); postLegacySynchronousMessage returns ExceptionOr<JSC::JSValue>. A new test grabs iframe1.contentWindow.window.webkit.messageHandlers.testhandler1 from a cross-origin iframe and expects the post to fail.
Same-origin invariant enforced at capability-acquisition time but not re-checked at capability-use time, allowing a captured handler reference to be exercised after the underlying frame's origin has changed.
Background
WKUserContentController lets host apps register named JS-callable bridges; in script, window.webkit.messageHandlers.<name>.postMessage(value) delivers value to the app's WKScriptMessageHandler. The UserMessageHandler C++ object backing each name is constructed per-frame and bound to that LocalFrame via FrameDestructionObserver. When two frames are same-origin, scripts in one can directly reach JS objects on the other's global; navigating a frame replaces its Document/origin but does not automatically invalidate JS object references already captured by other scripts.
Analysis
postMessage checked only that m_descriptor was attached, then forwarded directly to descriptor->didPostMessage(*this, globalObject, ...). The handler is bound at construction to a specific frame, and the host app identifies messages by the handler's frame at delivery time. Nothing on the call path verified that the JSGlobalObject actually invoking postMessage belonged to the same security origin as the frame the handler is attached to.
Aaa Aaaaaaa Aaaaaaaa a Aaaaaa Aa Aaaaaa a Aaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaa Aaaaaaaaaaa Aaaa Aaa Aaaaaa Aaa Aaaaaaa Aaa Aaaaaaaaaa Aaaaaaaaaa Aaaaaaaaaa Aa Aaaaaaaaaaa Aa Aaa Aaaaaa Aa Aaaaaa a Aaaaaa Aaa Aaaaaaaa Aaaaaaaaa Aaaaaaaa Aa Aaa Aaaaaa a Aaa Aaaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaa Aaa Aaaaaaaa Aaaaaaaaaaaaaa Aaaaaaa Aa Aaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aa Aa Aaa Aaaa Aaaaa Aaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaa a Aaaaaaa Aaaaaaaaaa Aa Aaaaa Aa
Aaaa Aaaaaaaaaaaaa Aaaaaaa Aaa Aaaaaaaaaaa Aaaaaaaa Aaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaa Aaaa Aaaa Aaaaaaaaa Aaa Aaaaaaaaaaaaaaaaaa Aa Aaaa Aaaaa Aaaaaaaaa a a Aaaaaaa Aaa Aaaaaaaa a Aaaaaaaaaa Aaaaaaa Aaaa Aa Aaa Aaa Aaaaaaa Aaa Aaaaaaa a Aa Aaaaaaaaaaaaaa Aaaaaaaa Aaa Aaaaa Aaaaaaaa Aaaaaaaaaa Aaaaaaaaaaaaa Aa Aaaaaaaaa Aaaaaa Aaaaaaaa Aaa Aaaaa Aaa Aa Aa Aaaaaaaaa Aaa Aaaaaa Aaaaaa Aaaa Aaa Aaaa Aaaaaaaaaaaaaaaa Aa Aaaa Aaaa Aaa Aaaaaaa Aaaaaaa Aaa Aaaa Aaaaa Aaaaaaa
🔒Where the same-origin policy is enforced for this capability — and what changes for an attacker who captured a reference at the right moment — is examined in detail.
Subscribe to read more
Audit directions
a Aaaaaaaaaaaa Aaaaaaa Aaaaa Aa a Aaaaaaaaaaaaaaaaaaaaaaa Aa Aaaaaaaaaaaa Aaaaa Aaaaaaa Aaaaa Aaaaaaaa Aaa Aaaaaaa Aaaaaaa Aaaaaaaaa Aaaaaa Aaaaaaa Aaa Aaaaaaa Aaaaaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaa Aaaaaa Aaaaaa Aaaaaa Aaaaaaaaaaaaaaaaaaaaa Aaa Aaaa a Aaaaaaaaaaaaaaaaaaaaaaa Aaaaaa Aaaa Aaaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaa Aaa Aaaaa Aaaaaaa Aaaaaaa Aaaaaaa
a Aaaaaaaaaaaaaaaaaa Aaaaaaaaaa Aaaaaaaaa Aaaa Aa Aaaaaaaaaaaaaa Aaaaaa Aaaaaaaaaaaaaaaaa Aaaaaaaaaaaa Aaaa a Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaaaaaaaaaaa Aaaaaa Aaa Aaaaaaaaaaa
a Aaaaaaaaaa Aaaaa Aaaaaaaaa Aaaaa Aa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaa Aaaaaaaaa Aa Aaaaaaaaaaaa Aaaa Aaa Aaaa Aa Aaa Aaa Aaaa Aaaa Aaaaaaaaa Aaaaaa Aaaaaaaaaaaaa Aa Aaaa Aaaaaa
a Aaaaaaaaa Aaaaaaaaaaaa Aaaaa Aa Aaaaaaaaaaaa Aaaaaaa Aaaaaa Aaaaaaaaaaaaaaaaaaaa Aaaa Aaaaaaa Aaa Aaaaaaaaaaaaaaaaaaaa Aaaaaaa Aaaa Aaaaaaa Aa Aaaaaaaa Aaaaaaaaa Aaaaaaa Aaaaaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
🔒Several reusable audit patterns identified for capability-style JS bridges across WebCore, with concrete starting points for variant discovery.
Subscribe to read more