Skip to content

Commit 301bff4

Browse files
the-chenergyJonWBedard
authored andcommitted
A compromised Web Content process should not be able to start Web Inspector
rdar://98891055 https://bugs.webkit.org/show_bug.cgi?id=283092 Reviewed by Ryosuke Niwa and BJ Burg. There currently exists a message WebInspectorUIProxy::OpenLocalInspectorFrontend, which the web process sends to the UI process to show Web Inspector for the current web page. This introduces security risks as a compromised website may find its way to send arbitrary messages to the UI process, opening Web Inspector and weakening the web content sandbox. The reason this message exists is because there are useful ways the web process needs to open Web Inspector with initiative. Normally, Web Inspector is opened via one of the Develop menu's items, which is controlled by the UI process. However, Web Inspector can also be opened without being prompted by the UI process first, in these places: 1. In a web page's context menu, the "Inspect Element" option 2. Inside Web Inspector, if the Debug UI is enabled, on the top right corner, a button to open inspector^2 3. In WebKitTestRunner, via the TestRunner::showWebInspector function This patch makes it so that web process can no longer send a message to a UI process to open Web Inspector. This means web process cannot open Web Inspector at will -- it must be either due to the UI process's demand, or it's in one of the above three cases. More details below. I have tested that this change preserves the above three special cases and does prevent the web page from opening Web Inspector at will. - Cases #1 and #2 can be tested from the UI. - Case #3 can be tested with a WebKit test involving Web Inspector. I ran the test LayoutTests/inspector/console/js-completions.html, where I saw the test crashing without special treatment for this case. - To verify that the web page can't open Web Inspector, I followed the reproduction steps from the Radar and saw Web Inspector no longer opens, and opening the external URL also failed as expected. * Source/WebKit/UIProcess/Inspector/WebInspectorUIProxy.messages.in: * Source/WebKit/UIProcess/Inspector/WebInspectorUIProxy.h: * Source/WebKit/UIProcess/Inspector/WebInspectorUIProxy.cpp: (WebKit::WebInspectorUIProxy::connect): - If the UI process wants to open Web Inspector, it sends a WebInspector::Show command to the web process. This patch makes that command take an async reply, so that the anticipated WebInspectorUIProxy::OpenLocalInspectorFrontend message from the web process can now be delivered through that async reply instead. This ensures that OpenLocalInspectorFrontend can only be done when initiated from the UI process (due to user interaction). (WebKit::WebInspectorUIProxy::markAsUnderTest): (WebKit::WebInspectorUIProxy::openLocalInspectorFrontend): (WebKit::WebInspectorUIProxy::closeFrontendPageAndWindow): - To avoid relying on the web process for potentially sensitive parameters, I reworked and removed the canAttach and underTest arguments from openLocalInspectorFrontend. These two values are now stored and managed in the UI process instead, instead of being passed from the web process all the time. - For canAttach, I noticed that the WebInspectorUIProxyMac::platformCanAttach method already implements the same logic as the web process's WebInspector::canAttachWindow. I filed https://webkit.org/b/283435 as a follow-up to clean up the webProcessCanAttach parameter, the canAttachWindow function in the web process, and potentially the m_attached field too, which all become obsolete due to this change. - I couldn't figure out what the `if (m_attached)` in canAttachWindow check does, and to me it had no effect, as this function is not called while inspector is open. - For underTest, I'm now letting the test runner directly set the flag on the WebInspectorUIProxy, as part of my fix to address case #3 from above. (WebKit::WebInspectorUIProxy::showConsole): (WebKit::WebInspectorUIProxy::showResources): (WebKit::WebInspectorUIProxy::showMainResourceForFrame): (WebKit::WebInspectorUIProxy::togglePageProfiling): - As the web process can longer call OpenLocalInspectorFrontend, call show/connect/openLocalInspectorFrontend here in the UI process instead. (WebKit::WebInspectorUIProxy::requestOpenLocalInspectorFrontend): - To preserve the open inspector^2 button (case #2 from above), we still maintain this message, but we ignore it unless it's for opening inspector^2, thus renaming the message as a request. This is all assuming that the Web Inspector is not a compromised web process, so we allow that message from it to come through. * Source/WebKit/WebProcess/Inspector/WebInspector.messages.in: * Source/WebKit/WebProcess/Inspector/WebInspector.h: * Source/WebKit/WebProcess/Inspector/WebInspector.cpp: (WebKit::WebInspector::show): - The Show message now takes an async reply, which is used to replace sending WebInspectorUIProxy::OpenLocalInspectorFrontend later. (WebKit::WebInspector::showConsole): (WebKit::WebInspector::showResources): (WebKit::WebInspector::showMainResourceForFrame): (WebKit::WebInspector::startPageProfiling): (WebKit::WebInspector::stopPageProfiling): - Calling inspectorController()->show() no longer does anything, since it's now the UI process's job to show Web Inspector first, for these functions to merely switch to the appropriate tabs. * Source/WebKit/WebProcess/Inspector/WebInspector.cpp: (WebKit::WebInspector::openLocalInspectorFrontend): * Source/WebKit/WebProcess/Inspector/WebInspectorClient.cpp: (WebKit::WebInspectorClient::openLocalFrontend): - Adapt to the command's reworked version. - This is maintained to allow the opening of inspector^2 from the web process (case #2 from above). For opening inspector^1, this message will be ignored by the UI process. * Source/WebKit/UIProcess/WebPageProxy.cpp: (WebKit::WebPageProxy::contextMenuItemSelected): - When the "Inspect Element" context menu item is selected (case #1 from above), since the web process may not be privileged to open Web Inspector, handle the showing of inspector here in UI process. * Tools/WebKitTestRunner/InjectedBundle/TestRunner.cpp: (WTR::TestRunner::showWebInspector): * Tools/WebKitTestRunner/TestInvocation.cpp: (WTR::TestInvocation::didReceiveMessageFromInjectedBundle): * Source/WebKit/UIProcess/API/C/WKPagePrivate.h: * Source/WebKit/UIProcess/API/C/WKPage.cpp: (WKPageShowWebInspectorForTesting): - Preserve letting the WebKitTestRunner open Web Inspector (case #3 from above). - Adapt to the change that we now also let the UI process know about the underTest flag for case #3, rather than letting UI process rely on the value reported by the web process. * Source/WebKit/WebProcess/InjectedBundle/API/c/WKBundlePage.h: * Source/WebKit/WebProcess/InjectedBundle/API/c/WKBundlePage.cpp: (WKBundlePageShowInspectorForTest): Deleted. - No longer used due to my special fix for case #3. Originally-landed-as: 283286.537@safari-7620-branch (694a9b5). rdar://144667626 Canonical link: https://commits.webkit.org/290260@main
1 parent ff08cab commit 301bff4

File tree

14 files changed

+59
-34
lines changed

14 files changed

+59
-34
lines changed

Source/WebKit/UIProcess/API/C/WKPage.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2952,6 +2952,13 @@ void WKPageSetAllowsRemoteInspection(WKPageRef pageRef, bool allow)
29522952
#endif
29532953
}
29542954

2955+
void WKPageShowWebInspectorForTesting(WKPageRef pageRef)
2956+
{
2957+
RefPtr<WebInspectorUIProxy> inspector = toImpl(pageRef)->inspector();
2958+
inspector->markAsUnderTest();
2959+
inspector->show();
2960+
}
2961+
29552962
void WKPageSetMediaVolume(WKPageRef pageRef, float volume)
29562963
{
29572964
CRASH_IF_SUSPENDED;

Source/WebKit/UIProcess/API/C/WKPagePrivate.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,8 @@ WK_EXPORT void WKPageSetControlledByAutomation(WKPageRef page, bool controlled);
9494
WK_EXPORT bool WKPageGetAllowsRemoteInspection(WKPageRef page);
9595
WK_EXPORT void WKPageSetAllowsRemoteInspection(WKPageRef page, bool allow);
9696

97+
WK_EXPORT void WKPageShowWebInspectorForTesting(WKPageRef page);
98+
9799
WK_EXPORT void WKPageSetMediaVolume(WKPageRef page, float volume);
98100
WK_EXPORT void WKPageSetMayStartMediaWhenInWindow(WKPageRef page, bool mayStartMedia);
99101

Source/WebKit/UIProcess/Inspector/WebInspectorUIProxy.cpp

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,12 @@ void WebInspectorUIProxy::connect()
156156

157157
Ref legacyMainFrameProcess = inspectedPage->legacyMainFrameProcess();
158158
legacyMainFrameProcess->send(Messages::WebInspectorInterruptDispatcher::NotifyNeedDebuggerBreak(), 0);
159-
legacyMainFrameProcess->send(Messages::WebInspector::Show(), m_inspectedPage->webPageIDInMainFrameProcess());
159+
legacyMainFrameProcess->sendWithAsyncReply(
160+
Messages::WebInspector::Show(),
161+
[this, protectedThis = Ref { *this }] {
162+
openLocalInspectorFrontend();
163+
},
164+
m_inspectedPage->webPageIDInMainFrameProcess());
160165
}
161166

162167
void WebInspectorUIProxy::show()
@@ -265,7 +270,7 @@ void WebInspectorUIProxy::showConsole()
265270
if (!inspectedPage)
266271
return;
267272

268-
createFrontendPage();
273+
show();
269274

270275
inspectedPage->protectedLegacyMainFrameProcess()->send(Messages::WebInspector::ShowConsole(), inspectedPage->webPageIDInMainFrameProcess());
271276
}
@@ -276,7 +281,7 @@ void WebInspectorUIProxy::showResources()
276281
if (!inspectedPage)
277282
return;
278283

279-
createFrontendPage();
284+
show();
280285

281286
inspectedPage->protectedLegacyMainFrameProcess()->send(Messages::WebInspector::ShowResources(), inspectedPage->webPageIDInMainFrameProcess());
282287
}
@@ -287,7 +292,7 @@ void WebInspectorUIProxy::showMainResourceForFrame(WebCore::FrameIdentifier fram
287292
if (!inspectedPage)
288293
return;
289294

290-
createFrontendPage();
295+
show();
291296

292297
inspectedPage->sendToProcessContainingFrame(frameID, Messages::WebInspector::ShowMainResourceForFrame(frameID));
293298
}
@@ -392,6 +397,8 @@ void WebInspectorUIProxy::togglePageProfiling()
392397
if (!m_inspectedPage)
393398
return;
394399

400+
show();
401+
395402
RefPtr inspectedPage = m_inspectedPage.get();
396403
if (m_isProfilingPage)
397404
inspectedPage->protectedLegacyMainFrameProcess()->send(Messages::WebInspector::StopPageProfiling(), inspectedPage->webPageIDInMainFrameProcess());
@@ -453,7 +460,16 @@ void WebInspectorUIProxy::createFrontendPage()
453460
#endif
454461
}
455462

456-
void WebInspectorUIProxy::openLocalInspectorFrontend(bool canAttach, bool underTest)
463+
void WebInspectorUIProxy::requestOpenLocalInspectorFrontend()
464+
{
465+
// Prevent a compromised malicious web page from opening Web Inspector at will.
466+
if (!m_underTest && inspectionLevel() < 2)
467+
return;
468+
469+
openLocalInspectorFrontend();
470+
}
471+
472+
void WebInspectorUIProxy::openLocalInspectorFrontend()
457473
{
458474
RefPtr inspectedPage = m_inspectedPage.get();
459475
if (!inspectedPage)
@@ -467,7 +483,6 @@ void WebInspectorUIProxy::openLocalInspectorFrontend(bool canAttach, bool underT
467483
return;
468484
}
469485

470-
m_underTest = underTest;
471486
createFrontendPage();
472487

473488
RefPtr inspectorPage = m_inspectorPage.get();
@@ -482,7 +497,10 @@ void WebInspectorUIProxy::openLocalInspectorFrontend(bool canAttach, bool underT
482497
inspectedPage->inspectorController().connectFrontend(*this);
483498

484499
if (!m_underTest) {
485-
m_canAttach = platformCanAttach(canAttach);
500+
// FIXME <https://webkit.org/b/283435>: Remove the webProcessCanAttach argument from platformCanAttach.
501+
// The value canAttach in the web process is no longer used or respected.
502+
const bool webProcessCanAttach = false;
503+
m_canAttach = platformCanAttach(webProcessCanAttach);
486504
m_isAttached = shouldOpenAttached();
487505
m_attachmentSide = static_cast<AttachmentSide>(protectedInspectorPagePreferences()->inspectorAttachmentSide());
488506

@@ -607,7 +625,6 @@ void WebInspectorUIProxy::closeFrontendPageAndWindow()
607625

608626
m_isAttached = false;
609627
m_canAttach = false;
610-
m_underTest = false;
611628

612629
platformCloseFrontendPageAndWindow();
613630
}

Source/WebKit/UIProcess/Inspector/WebInspectorUIProxy.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,7 @@ class WebInspectorUIProxy
199199
void toggleElementSelection();
200200

201201
bool isUnderTest() const { return m_underTest; }
202+
void markAsUnderTest() { m_underTest = true; }
202203

203204
void setDiagnosticLoggingAvailable(bool);
204205

@@ -264,9 +265,10 @@ class WebInspectorUIProxy
264265
#endif
265266

266267
// Called by WebInspectorUIProxy messages
267-
void openLocalInspectorFrontend(bool canAttach, bool underTest);
268+
void requestOpenLocalInspectorFrontend();
268269
void setFrontendConnection(IPC::Connection::Handle&&);
269270

271+
void openLocalInspectorFrontend();
270272
void sendMessageToBackend(const String&);
271273
void frontendLoaded();
272274
void didClose();

Source/WebKit/UIProcess/Inspector/WebInspectorUIProxy.messages.in

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
DispatchedTo=UI
2727
]
2828
messages -> WebInspectorUIProxy {
29-
OpenLocalInspectorFrontend(bool canAttach, bool underTest)
29+
RequestOpenLocalInspectorFrontend()
3030
SetFrontendConnection(IPC::ConnectionHandle connectionHandle)
3131

3232
SendMessageToBackend(String message)

Source/WebKit/UIProcess/WebPageProxy.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10056,6 +10056,12 @@ void WebPageProxy::contextMenuItemSelected(const WebContextMenuItemData& item)
1005610056
case ContextMenuItemTagShowColors:
1005710057
showColorPanel();
1005810058
return;
10059+
10060+
case ContextMenuItemTagInspectElement:
10061+
// The web process can no longer demand Web Inspector to show, so handle that part here.
10062+
m_inspector->show();
10063+
// The actual element-selection is still handled in the web process, so we break instead of return.
10064+
break;
1005910065
#endif // PLATFORM(MAC)
1006010066

1006110067
case ContextMenuItemTagShowSpellingPanel:

Source/WebKit/WebProcess/InjectedBundle/API/c/WKBundlePage.cpp

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -438,11 +438,6 @@ void WKBundlePageListenForLayoutMilestones(WKBundlePageRef pageRef, WKLayoutMile
438438
WebKit::toImpl(pageRef)->listenForLayoutMilestones(WebKit::toLayoutMilestones(milestones));
439439
}
440440

441-
void WKBundlePageShowInspectorForTest(WKBundlePageRef page)
442-
{
443-
WebKit::toImpl(page)->inspector()->show();
444-
}
445-
446441
void WKBundlePageCloseInspectorForTest(WKBundlePageRef page)
447442
{
448443
WebKit::toImpl(page)->inspector()->close();

Source/WebKit/WebProcess/InjectedBundle/API/c/WKBundlePage.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,6 @@ WK_EXPORT double WKBundlePageGetBackingScaleFactor(WKBundlePageRef page);
9999

100100
WK_EXPORT void WKBundlePageListenForLayoutMilestones(WKBundlePageRef page, WKLayoutMilestones milestones);
101101

102-
WK_EXPORT void WKBundlePageShowInspectorForTest(WKBundlePageRef page);
103102
WK_EXPORT void WKBundlePageCloseInspectorForTest(WKBundlePageRef page);
104103
WK_EXPORT void WKBundlePageEvaluateScriptInInspectorForTest(WKBundlePageRef page, WKStringRef script);
105104

Source/WebKit/WebProcess/Inspector/WebInspector.messages.in

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
DispatchedTo=WebContent
2626
]
2727
messages -> WebInspector {
28-
Show()
28+
Show() -> () Async
2929
Close()
3030

3131
SetAttached(bool attached)

Source/WebKit/WebProcess/Inspector/WebInspectorClient.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ void WebInspectorClient::frontendCountChanged(unsigned count)
102102
Inspector::FrontendChannel* WebInspectorClient::openLocalFrontend(InspectorController* controller)
103103
{
104104
if (RefPtr page = m_page.get())
105-
page->inspector()->openLocalInspectorFrontend(controller->isUnderTest());
105+
page->inspector()->openLocalInspectorFrontend();
106106
return nullptr;
107107
}
108108

0 commit comments

Comments
 (0)