BroadcastChannelParent: circular principal validation allows cross-origin channel join from compromised content process
Categories
(Core :: DOM: postMessage, defect)
Tracking
()
People
(Reporter: lukefrancis09, Unassigned)
Details
(Keywords: ai-involved, reporter-external, Whiteboard: [client-bounty-form])
A compromised content process can join any origin's BroadcastChannel. The PBroadcastChannelConstructor handler registers the actor before principal validation runs, and the validation itself is circular — it checks the attacker-supplied PrincipalInfo against the attacker-supplied origin string. Since the attacker controls both, the check always passes. ValidatePrincipal is never called.
Bug:
Registration before validation — BroadcastChannelParent's constructor (dom/broadcastchannel/BroadcastChannelParent.cpp:21-27) calls mService->RegisterActor(this, mOriginChannelKey) immediately. The actor is live and receiving messages before RecvPBroadcastChannelConstructor even dispatches the async principal check.
Circular check — CheckPrincipalRunnable::Run() converts the IPC-supplied mPrincipalInfo to a principal, extracts its origin, and compares it to the IPC-supplied mOrigin. Both values come from the content process. A compromised process constructs a valid ContentPrincipalInfo for any target origin and sets aOrigin to match — the check passes trivially.
The code at BackgroundParentImpl.cpp line 802-804 contains a TODO acknowledging this should be synchronous.
No process identity check — ValidatePrincipal is used in ~15 other ContentParent handlers to verify that a content process is authorized to claim a given principal. It is completely absent from the BroadcastChannel path. BackgroundParent::GetContentParentHandle(this) is available but unused.
Delivery — BroadcastChannelService::PostMessage routes messages by originChannelKey match only. No origin check at delivery time.
Attack Scenario
- Compromised content process (evil.com) sends
PBroadcastChannelConstructorwithaPrincipalInfo=ContentPrincipalInfo{spec="https://bank.com/"},aOrigin="https://bank.com",aChannel="session-sync" - Parent creates actor, constructor registers it — actor is live
CheckPrincipalRunnabledispatches async, converts PrincipalInfo → origin"https://bank.com", compares withaOrigin"https://bank.com"→ passes- Attacker receives all messages on
bank.com's"session-sync"channel and can inject messages
Impact
Cross-origin BroadcastChannel eavesdropping and injection. Applications using BroadcastChannel for cross-tab session sync, auth state, or coordination are exposed. Bypasses Fission's process-per-site isolation at the BroadcastChannel layer — the process doesn't need to host the target origin, it just claims to.
Suggested Fix
- Move
RegisterActor()from the constructor to a callback after principal validation succeeds - Replace the circular origin-string check with
ValidatePrincipal(orValidatePrincipalCouldPotentiallyBeLoadedBy) using theContentParentassociated with theBackgroundParentImpl
let me know if you need any additional information, happy to provide it!
Updated•8 months ago
|
Updated•8 months ago
|
Updated•8 months ago
|
Updated•1 month ago
|
Description
•