Title: [254322] trunk
Revision
254322
Author
[email protected]
Date
2020-01-09 19:38:43 -0800 (Thu, 09 Jan 2020)

Log Message

Block cross-site top-frame navigations from untrusted iframes
https://bugs.webkit.org/show_bug.cgi?id=206027
<rdar://problem/58320516>

Reviewed by Geoffrey Garen.

Source/WebCore:

Block cross-site top-frame navigations from untrusted iframes, unless they have a user gesture.
We already consider third-party iframes as untrusted, we now also treat first-party iframes
as untrusted if they are loaded both third-party scripts & iframes.

Test: http/tests/security/block-top-level-navigations-by-untrusted-first-party-iframes.html

* dom/Document.cpp:
(WebCore::Document::canNavigate):
(WebCore::Document::willLoadScriptElement):
(WebCore::Document::willLoadFrameElement):
(WebCore::Document::isNavigationBlockedByThirdPartyIFrameRedirectBlocking):
* dom/Document.h:
* dom/ScriptElement.cpp:
(WebCore::ScriptElement::requestClassicScript):
* html/HTMLFrameElementBase.cpp:
(WebCore::HTMLFrameElementBase::openURL):

LayoutTests:

Add layout test coverage.

* http/tests/security/block-top-level-navigations-by-third-party-iframes-expected.txt:
* http/tests/security/block-top-level-navigations-by-untrusted-first-party-iframes-expected.txt: Added.
* http/tests/security/block-top-level-navigations-by-untrusted-first-party-iframes.html: Added.
* http/tests/security/resources/navigate-top-level-frame-to-failure-page-untrusted-iframe.html: Added.
* http/tests/security/resources/navigate-top-to-error-page.js: Added.

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (254321 => 254322)


--- trunk/LayoutTests/ChangeLog	2020-01-10 03:08:35 UTC (rev 254321)
+++ trunk/LayoutTests/ChangeLog	2020-01-10 03:38:43 UTC (rev 254322)
@@ -1,3 +1,19 @@
+2020-01-09  Chris Dumez  <[email protected]>
+
+        Block cross-site top-frame navigations from untrusted iframes
+        https://bugs.webkit.org/show_bug.cgi?id=206027
+        <rdar://problem/58320516>
+
+        Reviewed by Geoffrey Garen.
+
+        Add layout test coverage.
+
+        * http/tests/security/block-top-level-navigations-by-third-party-iframes-expected.txt:
+        * http/tests/security/block-top-level-navigations-by-untrusted-first-party-iframes-expected.txt: Added.
+        * http/tests/security/block-top-level-navigations-by-untrusted-first-party-iframes.html: Added.
+        * http/tests/security/resources/navigate-top-level-frame-to-failure-page-untrusted-iframe.html: Added.
+        * http/tests/security/resources/navigate-top-to-error-page.js: Added.
+
 2020-01-09  Diego Pino Garcia  <[email protected]>
 
         [GTK] Unreviewed test gardening

Modified: trunk/LayoutTests/http/tests/security/block-top-level-navigations-by-third-party-iframes-expected.txt (254321 => 254322)


--- trunk/LayoutTests/http/tests/security/block-top-level-navigations-by-third-party-iframes-expected.txt	2020-01-10 03:08:35 UTC (rev 254321)
+++ trunk/LayoutTests/http/tests/security/block-top-level-navigations-by-third-party-iframes-expected.txt	2020-01-10 03:38:43 UTC (rev 254322)
@@ -1,7 +1,7 @@
-CONSOLE MESSAGE: line 6: Unsafe _javascript_ attempt to initiate navigation for frame with URL 'http://127.0.0.1:8000/security/block-top-level-navigations-by-third-party-iframes.html' from frame with URL 'http://localhost:8000/security/resources/navigate-top-level-frame-to-failure-page.html'. The frame attempting navigation of the top-level window is cross-origin and the user has never interacted with the frame.
+CONSOLE MESSAGE: line 6: Unsafe _javascript_ attempt to initiate navigation for frame with URL 'http://127.0.0.1:8000/security/block-top-level-navigations-by-third-party-iframes.html' from frame with URL 'http://localhost:8000/security/resources/navigate-top-level-frame-to-failure-page.html'. The frame attempting navigation of the top-level window is cross-origin or untrusted and the user has never interacted with the frame.
 
 CONSOLE MESSAGE: line 6: SecurityError: The operation is insecure.
-CONSOLE MESSAGE: line 6: Unsafe _javascript_ attempt to initiate navigation for frame with URL 'http://127.0.0.1:8000/security/block-top-level-navigations-by-third-party-iframes.html' from frame with URL 'http://localhost:8000/security/resources/navigate-top-level-frame-to-failure-page.html'. The frame attempting navigation of the top-level window is cross-origin and the user has never interacted with the frame.
+CONSOLE MESSAGE: line 6: Unsafe _javascript_ attempt to initiate navigation for frame with URL 'http://127.0.0.1:8000/security/block-top-level-navigations-by-third-party-iframes.html' from frame with URL 'http://localhost:8000/security/resources/navigate-top-level-frame-to-failure-page.html'. The frame attempting navigation of the top-level window is cross-origin or untrusted and the user has never interacted with the frame.
 
 CONSOLE MESSAGE: line 6: SecurityError: The operation is insecure.
 Test blocking of suspicious top-level navigations by a third-party iframe

Added: trunk/LayoutTests/http/tests/security/block-top-level-navigations-by-untrusted-first-party-iframes-expected.txt (0 => 254322)


--- trunk/LayoutTests/http/tests/security/block-top-level-navigations-by-untrusted-first-party-iframes-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/http/tests/security/block-top-level-navigations-by-untrusted-first-party-iframes-expected.txt	2020-01-10 03:38:43 UTC (rev 254322)
@@ -0,0 +1,16 @@
+CONSOLE MESSAGE: Unsafe _javascript_ attempt to initiate navigation for frame with URL 'http://127.0.0.1:8000/security/block-top-level-navigations-by-untrusted-first-party-iframes.html' from frame with URL 'http://127.0.0.1:8000/security/resources/navigate-top-level-frame-to-failure-page-untrusted-iframe.html'. The frame attempting navigation of the top-level window is cross-origin or untrusted and the user has never interacted with the frame.
+
+CONSOLE MESSAGE: SecurityError: The operation is insecure.
+CONSOLE MESSAGE: Unsafe _javascript_ attempt to initiate navigation for frame with URL 'http://127.0.0.1:8000/security/block-top-level-navigations-by-untrusted-first-party-iframes.html' from frame with URL 'http://127.0.0.1:8000/security/resources/navigate-top-level-frame-to-failure-page-untrusted-iframe.html'. The frame attempting navigation of the top-level window is cross-origin or untrusted and the user has never interacted with the frame.
+
+CONSOLE MESSAGE: SecurityError: The operation is insecure.
+Test blocking of suspicious top-level navigations by a untrusted first-party iframe
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS All navigations by subframes have been blocked
+PASS successfullyParsed is true
+
+TEST COMPLETE
+ 

Added: trunk/LayoutTests/http/tests/security/block-top-level-navigations-by-untrusted-first-party-iframes.html (0 => 254322)


--- trunk/LayoutTests/http/tests/security/block-top-level-navigations-by-untrusted-first-party-iframes.html	                        (rev 0)
+++ trunk/LayoutTests/http/tests/security/block-top-level-navigations-by-untrusted-first-party-iframes.html	2020-01-10 03:38:43 UTC (rev 254322)
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html>
+<body>
+<script src=""
+<script>
+description("Test blocking of suspicious top-level navigations by a untrusted first-party iframe");
+jsTestIsAsync = true;
+_onload_ = () => {
+    setTimeout(() => {
+        document.getElementById('testFrame').src = ""
+        setTimeout(() => {
+            testPassed("All navigations by subframes have been blocked");
+            finishJSTest();
+        }, 1000);
+    }, 10);
+}
+</script>
+<iframe src=""
+<iframe id="testFrame"></iframe>
+</body>
+</html>

Added: trunk/LayoutTests/http/tests/security/resources/navigate-top-level-frame-to-failure-page-untrusted-iframe.html (0 => 254322)


--- trunk/LayoutTests/http/tests/security/resources/navigate-top-level-frame-to-failure-page-untrusted-iframe.html	                        (rev 0)
+++ trunk/LayoutTests/http/tests/security/resources/navigate-top-level-frame-to-failure-page-untrusted-iframe.html	2020-01-10 03:38:43 UTC (rev 254322)
@@ -0,0 +1,8 @@
+<html>
+<body>
+Success! The navigation was blocked
+<iframe src=""
+<script src=""
+</script>
+</body>
+</html>

Added: trunk/LayoutTests/http/tests/security/resources/navigate-top-to-error-page.js (0 => 254322)


--- trunk/LayoutTests/http/tests/security/resources/navigate-top-to-error-page.js	                        (rev 0)
+++ trunk/LayoutTests/http/tests/security/resources/navigate-top-to-error-page.js	2020-01-10 03:38:43 UTC (rev 254322)
@@ -0,0 +1 @@
+top.location = "http://localhost:8000/security/resources/should-not-have-loaded.html";

Modified: trunk/Source/WebCore/ChangeLog (254321 => 254322)


--- trunk/Source/WebCore/ChangeLog	2020-01-10 03:08:35 UTC (rev 254321)
+++ trunk/Source/WebCore/ChangeLog	2020-01-10 03:38:43 UTC (rev 254322)
@@ -1,3 +1,28 @@
+2020-01-09  Chris Dumez  <[email protected]>
+
+        Block cross-site top-frame navigations from untrusted iframes
+        https://bugs.webkit.org/show_bug.cgi?id=206027
+        <rdar://problem/58320516>
+
+        Reviewed by Geoffrey Garen.
+
+        Block cross-site top-frame navigations from untrusted iframes, unless they have a user gesture.
+        We already consider third-party iframes as untrusted, we now also treat first-party iframes
+        as untrusted if they are loaded both third-party scripts & iframes.
+
+        Test: http/tests/security/block-top-level-navigations-by-untrusted-first-party-iframes.html
+
+        * dom/Document.cpp:
+        (WebCore::Document::canNavigate):
+        (WebCore::Document::willLoadScriptElement):
+        (WebCore::Document::willLoadFrameElement):
+        (WebCore::Document::isNavigationBlockedByThirdPartyIFrameRedirectBlocking):
+        * dom/Document.h:
+        * dom/ScriptElement.cpp:
+        (WebCore::ScriptElement::requestClassicScript):
+        * html/HTMLFrameElementBase.cpp:
+        (WebCore::HTMLFrameElementBase::openURL):
+
 2020-01-09  Andres Gonzalez  <[email protected]>
 
         Disable accessibility isolated tree for LayoutTests.

Modified: trunk/Source/WebCore/dom/Document.cpp (254321 => 254322)


--- trunk/Source/WebCore/dom/Document.cpp	2020-01-10 03:08:35 UTC (rev 254321)
+++ trunk/Source/WebCore/dom/Document.cpp	2020-01-10 03:38:43 UTC (rev 254322)
@@ -3366,7 +3366,7 @@
         return false;
 
     if (isNavigationBlockedByThirdPartyIFrameRedirectBlocking(*targetFrame, destinationURL)) {
-        printNavigationErrorMessage(*targetFrame, url(), "The frame attempting navigation of the top-level window is cross-origin and the user has never interacted with the frame."_s);
+        printNavigationErrorMessage(*targetFrame, url(), "The frame attempting navigation of the top-level window is cross-origin or untrusted and the user has never interacted with the frame."_s);
         return false;
     }
 
@@ -3456,6 +3456,16 @@
     return false;
 }
 
+void Document::willLoadScriptElement(const URL& scriptURL)
+{
+    m_hasLoadedThirdPartyScript = m_hasLoadedThirdPartyScript || !securityOrigin().isSameOriginAs(SecurityOrigin::create(scriptURL));
+}
+
+void Document::willLoadFrameElement(const URL& frameURL)
+{
+    m_hasLoadedThirdPartyFrame = m_hasLoadedThirdPartyFrame || !securityOrigin().isSameOriginAs(SecurityOrigin::create(frameURL));
+}
+
 // Prevent cross-site top-level redirects from third-party iframes unless the user has ever interacted with the frame.
 bool Document::isNavigationBlockedByThirdPartyIFrameRedirectBlocking(Frame& targetFrame, const URL& destinationURL)
 {
@@ -3475,8 +3485,9 @@
     if (sandboxFlags() != SandboxNone)
         return false;
 
-    // Only prevent navigations by third-party iframes.
-    if (canAccessAncestor(securityOrigin(), &targetFrame))
+    // Only prevent navigations by third-party iframes or untrusted first-party iframes.
+    bool isUntrustedIframe = m_hasLoadedThirdPartyScript && m_hasLoadedThirdPartyFrame;
+    if (canAccessAncestor(securityOrigin(), &targetFrame) && !isUntrustedIframe)
         return false;
 
     // Only prevent cross-site navigations.

Modified: trunk/Source/WebCore/dom/Document.h (254321 => 254322)


--- trunk/Source/WebCore/dom/Document.h	2020-01-10 03:08:35 UTC (rev 254321)
+++ trunk/Source/WebCore/dom/Document.h	2020-01-10 03:38:43 UTC (rev 254322)
@@ -1327,6 +1327,9 @@
     SecurityOrigin& securityOrigin() const { return *SecurityContext::securityOrigin(); }
     SecurityOrigin& topOrigin() const final { return topDocument().securityOrigin(); }
 
+    void willLoadScriptElement(const URL&);
+    void willLoadFrameElement(const URL&);
+
     Ref<FontFaceSet> fonts();
 
     void ensurePlugInsInjectedScript(DOMWrapperWorld&);
@@ -2055,6 +2058,8 @@
     bool m_isRunningUserScripts { false };
     bool m_mayBeDetachedFromFrame { true };
     bool m_shouldPreventEnteringBackForwardCacheForTesting { false };
+    bool m_hasLoadedThirdPartyScript { false };
+    bool m_hasLoadedThirdPartyFrame { false };
 #if ENABLE(APPLE_PAY)
     bool m_hasStartedApplePaySession { false };
 #endif

Modified: trunk/Source/WebCore/dom/ScriptElement.cpp (254321 => 254322)


--- trunk/Source/WebCore/dom/ScriptElement.cpp	2020-01-10 03:08:35 UTC (rev 254321)
+++ trunk/Source/WebCore/dom/ScriptElement.cpp	2020-01-10 03:38:43 UTC (rev 254322)
@@ -290,7 +290,10 @@
             scriptCharset(),
             m_element.localName(),
             m_element.isInUserAgentShadowTree());
-        if (script->load(m_element.document(), m_element.document().completeURL(sourceURL))) {
+
+        auto scriptURL = m_element.document().completeURL(sourceURL);
+        m_element.document().willLoadScriptElement(scriptURL);
+        if (script->load(m_element.document(), scriptURL)) {
             m_loadableScript = WTFMove(script);
             m_isExternalScript = true;
         }

Modified: trunk/Source/WebCore/html/HTMLFrameElementBase.cpp (254321 => 254322)


--- trunk/Source/WebCore/html/HTMLFrameElementBase.cpp	2020-01-10 03:08:35 UTC (rev 254321)
+++ trunk/Source/WebCore/html/HTMLFrameElementBase.cpp	2020-01-10 03:38:43 UTC (rev 254322)
@@ -93,6 +93,8 @@
     if (!parentFrame)
         return;
 
+    document().willLoadFrameElement(parentFrame->document()->completeURL(m_URL));
+
     String frameName = getNameAttribute();
     if (frameName.isNull() && UNLIKELY(document().settings().needsFrameNameFallbackToIdQuirk()))
         frameName = getIdAttribute();
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to