window.name leaks information across domains
authoraestes@apple.com <aestes@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 29 Nov 2016 18:40:55 +0000 (18:40 +0000)
committeraestes@apple.com <aestes@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 29 Nov 2016 18:40:55 +0000 (18:40 +0000)
https://bugs.webkit.org/show_bug.cgi?id=158216
<rdar://problem/14548481>

Reviewed by Brent Fulgham.

Source/WebCore:

When updating the history after a cross-origin navigation, the HTML Standard says:

"If the browsing context is a top-level browsing context, but not an auxiliary browsing
context, then set the browsing context's name to the empty string."

https://html.spec.whatwg.org/multipage/browsers.html#resetBCName

Tests: http/tests/security/window-name-after-cross-origin-aux-frame-navigation.html
       http/tests/security/window-name-after-cross-origin-main-frame-navigation.html
       http/tests/security/window-name-after-cross-origin-sub-frame-navigation.html
       http/tests/security/window-name-after-same-origin-aux-frame-navigation.html
       http/tests/security/window-name-after-same-origin-main-frame-navigation.html
       http/tests/security/window-name-after-same-origin-sub-frame-navigation.html

* loader/FrameLoader.cpp:
(WebCore::shouldClearWindowName): Returns true if frame is a main frame with no opener and
newDocument does not have the same origin as the frame's current document.
(WebCore::FrameLoader::clear): Changed to set m_frame's name to nullAtom if
clearWindowProperties and shouldClearWindowName() are true.
* page/SecurityOrigin.cpp:
(WebCore::SecurityOrigin::canAccessStorage): Changed to call isSameOriginAs() and check
m_universalAccess.
(WebCore::SecurityOrigin::isSameOriginAs): Renamed from isThirdParty(); removed the check
for m_universalAccess.
(WebCore::SecurityOrigin::isThirdParty): Renamed to isSameOriginAs().
* page/SecurityOrigin.h: Renamed isThirdParty() to isSameOriginAs() and made it public.

LayoutTests:

* fast/events/pageshow-pagehide-on-back-uncached-expected.txt: Updated to account for the
main frame no longer having a name.
* fast/events/pageshow-pagehide-on-back-uncached.html: Updated to use the History API
instead of relying on window.name being retained after a cross-origin navigation.
* fast/events/script-tests/onunload-back-to-page-cache.js:
(onpageshow): Ditto.
* http/tests/security/resources/log-window-name.html: Added.
* http/tests/security/resources/window-name-test.html: Added.
* http/tests/security/window-name-after-cross-origin-aux-frame-navigation-expected.txt: Added.
* http/tests/security/window-name-after-cross-origin-aux-frame-navigation.html: Added.
* http/tests/security/window-name-after-cross-origin-main-frame-navigation-expected.txt: Added.
* http/tests/security/window-name-after-cross-origin-main-frame-navigation.html: Added.
* http/tests/security/window-name-after-cross-origin-sub-frame-navigation-expected.txt: Added.
* http/tests/security/window-name-after-cross-origin-sub-frame-navigation.html: Added.
* http/tests/security/window-name-after-same-origin-aux-frame-navigation-expected.txt: Added.
* http/tests/security/window-name-after-same-origin-aux-frame-navigation.html: Added.
* http/tests/security/window-name-after-same-origin-main-frame-navigation-expected.txt: Added.
* http/tests/security/window-name-after-same-origin-main-frame-navigation.html: Added.
* http/tests/security/window-name-after-same-origin-sub-frame-navigation-expected.txt: Added.
* http/tests/security/window-name-after-same-origin-sub-frame-navigation.html: Added.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@209076 268f45cc-cd09-0410-ab3c-d52691b4dbfc

22 files changed:
LayoutTests/ChangeLog
LayoutTests/fast/events/pageshow-pagehide-on-back-uncached-expected.txt
LayoutTests/fast/events/pageshow-pagehide-on-back-uncached.html
LayoutTests/fast/events/script-tests/onunload-back-to-page-cache.js
LayoutTests/http/tests/security/resources/log-window-name.html [new file with mode: 0644]
LayoutTests/http/tests/security/resources/window-name-test.html [new file with mode: 0644]
LayoutTests/http/tests/security/window-name-after-cross-origin-aux-frame-navigation-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/security/window-name-after-cross-origin-aux-frame-navigation.html [new file with mode: 0644]
LayoutTests/http/tests/security/window-name-after-cross-origin-main-frame-navigation-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/security/window-name-after-cross-origin-main-frame-navigation.html [new file with mode: 0644]
LayoutTests/http/tests/security/window-name-after-cross-origin-sub-frame-navigation-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/security/window-name-after-cross-origin-sub-frame-navigation.html [new file with mode: 0644]
LayoutTests/http/tests/security/window-name-after-same-origin-aux-frame-navigation-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/security/window-name-after-same-origin-aux-frame-navigation.html [new file with mode: 0644]
LayoutTests/http/tests/security/window-name-after-same-origin-main-frame-navigation-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/security/window-name-after-same-origin-main-frame-navigation.html [new file with mode: 0644]
LayoutTests/http/tests/security/window-name-after-same-origin-sub-frame-navigation-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/security/window-name-after-same-origin-sub-frame-navigation.html [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/loader/FrameLoader.cpp
Source/WebCore/page/SecurityOrigin.cpp
Source/WebCore/page/SecurityOrigin.h

index c822b9c..fed3a4f 100644 (file)
@@ -1,3 +1,32 @@
+2016-11-29  Andy Estes  <aestes@apple.com>
+
+        window.name leaks information across domains
+        https://bugs.webkit.org/show_bug.cgi?id=158216
+        <rdar://problem/14548481>
+
+        Reviewed by Brent Fulgham.
+
+        * fast/events/pageshow-pagehide-on-back-uncached-expected.txt: Updated to account for the
+        main frame no longer having a name.
+        * fast/events/pageshow-pagehide-on-back-uncached.html: Updated to use the History API
+        instead of relying on window.name being retained after a cross-origin navigation.
+        * fast/events/script-tests/onunload-back-to-page-cache.js:
+        (onpageshow): Ditto.
+        * http/tests/security/resources/log-window-name.html: Added.
+        * http/tests/security/resources/window-name-test.html: Added.
+        * http/tests/security/window-name-after-cross-origin-aux-frame-navigation-expected.txt: Added.
+        * http/tests/security/window-name-after-cross-origin-aux-frame-navigation.html: Added.
+        * http/tests/security/window-name-after-cross-origin-main-frame-navigation-expected.txt: Added.
+        * http/tests/security/window-name-after-cross-origin-main-frame-navigation.html: Added.
+        * http/tests/security/window-name-after-cross-origin-sub-frame-navigation-expected.txt: Added.
+        * http/tests/security/window-name-after-cross-origin-sub-frame-navigation.html: Added.
+        * http/tests/security/window-name-after-same-origin-aux-frame-navigation-expected.txt: Added.
+        * http/tests/security/window-name-after-same-origin-aux-frame-navigation.html: Added.
+        * http/tests/security/window-name-after-same-origin-main-frame-navigation-expected.txt: Added.
+        * http/tests/security/window-name-after-same-origin-main-frame-navigation.html: Added.
+        * http/tests/security/window-name-after-same-origin-sub-frame-navigation-expected.txt: Added.
+        * http/tests/security/window-name-after-same-origin-sub-frame-navigation.html: Added.
+
 2016-11-29  Wenson Hsieh  <wenson_hsieh@apple.com>
 
         Scroll snapping on Mac should use AppKit animations
index 1f5448b..3728f78 100644 (file)
@@ -1,8 +1,8 @@
 main frame - has 1 onunload handler(s)
 ALERT: window.onload
 ALERT: window.onpageshow, target = [object HTMLDocument], persisted = false
-CONSOLE MESSAGE: line 28: Use of window.alert is not allowed while unloading a page.
-main frame "pageshow/pagehide" - has 1 onunload handler(s)
+CONSOLE MESSAGE: line 27: Use of window.alert is not allowed while unloading a page.
+main frame - has 1 onunload handler(s)
 ALERT: window.onload
 ALERT: window.onpageshow, target = [object HTMLDocument], persisted = false
 Test pageshow/pagehide event behavior when navigating back to an uncached page.
index 48ec392..7a4baf6 100644 (file)
@@ -7,13 +7,12 @@ if (window.testRunner) {
 
 window.onload = function(evt) {
     alert("window.onload");
-    if (window.name == 'pageshow/pagehide') {
+    if (window.history.state == 'pageshow/pagehide') {
         // Returned back.
-        window.name = "";
         window.onpagehide = null;
         setTimeout(function() { if (window.testRunner) testRunner.notifyDone(); }, 10);
     } else {
-        window.name = "pageshow/pagehide";
+        window.history.replaceState("pageshow/pagehide", "");
         setTimeout('window.location = "data:text/html,<script>history.back();</scr" + "ipt>"', 0);
     }
 }
index ea75283..e258a18 100644 (file)
@@ -1,13 +1,13 @@
 description('Simulates flow from a page that\'s in the page cache to one that\'s not, and then back to the page cached page.');
 
 onpageshow = function(event) {
-    if (window.name == 'navigated') {
+    if (window.history.state == 'navigated') {
         testPassed('WebTiming asserts in FrameLoader.cpp did not fire');
         finishJSTest();
     } else {
         if (window.testRunner)
             testRunner.overridePreference('WebKitUsesPageCachePreferenceKey', 1);
-        setTimeout(function() {location.href = 'data:text/html,<script>onunload=function() {},onload=function(){window.name = \'navigated\';history.back();}<' + '/script>';}, 0);
+        setTimeout(function() {window.history.replaceState('navigated', ''); location.href = 'data:text/html,<script>onunload=function() {},onload=function(){history.back();}<' + '/script>';}, 0);
     }
 }
 
diff --git a/LayoutTests/http/tests/security/resources/log-window-name.html b/LayoutTests/http/tests/security/resources/log-window-name.html
new file mode 100644 (file)
index 0000000..3aac596
--- /dev/null
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<html>
+    <script>
+        console.log("window.name equals \"" + window.name + "\".");
+        testRunner.notifyDone();
+    </script>
+</html>
diff --git a/LayoutTests/http/tests/security/resources/window-name-test.html b/LayoutTests/http/tests/security/resources/window-name-test.html
new file mode 100644 (file)
index 0000000..c86d45f
--- /dev/null
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html>
+    <script>
+        var url = new URL(window.location.href);
+        url.hostname = url.search.substring(1);
+        url.pathname = "/security/resources/log-window-name.html";
+        url.search = "";
+
+        window.name = "test";
+        document.location = url.href;
+    </script>
+</html>
diff --git a/LayoutTests/http/tests/security/window-name-after-cross-origin-aux-frame-navigation-expected.txt b/LayoutTests/http/tests/security/window-name-after-cross-origin-aux-frame-navigation-expected.txt
new file mode 100644 (file)
index 0000000..17c69e9
--- /dev/null
@@ -0,0 +1,3 @@
+CONSOLE MESSAGE: line 10: window.name should equal "test" after a cross-origin auxiliary frame navigation.
+CONSOLE MESSAGE: line 4: window.name equals "test".
+
diff --git a/LayoutTests/http/tests/security/window-name-after-cross-origin-aux-frame-navigation.html b/LayoutTests/http/tests/security/window-name-after-cross-origin-aux-frame-navigation.html
new file mode 100644 (file)
index 0000000..8faa850
--- /dev/null
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html>
+    <script>
+        if (window.testRunner) {
+            testRunner.dumpAsText();
+            testRunner.waitUntilDone();
+            testRunner.setCanOpenWindows(true);
+        }
+
+        console.log("window.name should equal \"test\" after a cross-origin auxiliary frame navigation.");
+        window.open("resources/window-name-test.html?localhost");
+    </script>
+</html>
diff --git a/LayoutTests/http/tests/security/window-name-after-cross-origin-main-frame-navigation-expected.txt b/LayoutTests/http/tests/security/window-name-after-cross-origin-main-frame-navigation-expected.txt
new file mode 100644 (file)
index 0000000..6e63a4b
--- /dev/null
@@ -0,0 +1,3 @@
+CONSOLE MESSAGE: line 9: window.name should equal "" after a cross-origin main frame navigation.
+CONSOLE MESSAGE: line 4: window.name equals "".
+
diff --git a/LayoutTests/http/tests/security/window-name-after-cross-origin-main-frame-navigation.html b/LayoutTests/http/tests/security/window-name-after-cross-origin-main-frame-navigation.html
new file mode 100644 (file)
index 0000000..a703c45
--- /dev/null
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html>
+    <script>
+        if (window.testRunner) {
+            testRunner.dumpAsText();
+            testRunner.waitUntilDone();
+        }
+
+        console.log("window.name should equal \"\" after a cross-origin main frame navigation.");
+        document.location = "resources/window-name-test.html?localhost";
+    </script>
+</html>
diff --git a/LayoutTests/http/tests/security/window-name-after-cross-origin-sub-frame-navigation-expected.txt b/LayoutTests/http/tests/security/window-name-after-cross-origin-sub-frame-navigation-expected.txt
new file mode 100644 (file)
index 0000000..614c465
--- /dev/null
@@ -0,0 +1,3 @@
+CONSOLE MESSAGE: line 9: window.name should equal "test" after a cross-origin sub frame navigation.
+CONSOLE MESSAGE: line 4: window.name equals "test".
+
diff --git a/LayoutTests/http/tests/security/window-name-after-cross-origin-sub-frame-navigation.html b/LayoutTests/http/tests/security/window-name-after-cross-origin-sub-frame-navigation.html
new file mode 100644 (file)
index 0000000..1f934c4
--- /dev/null
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html>
+    <script>
+        if (window.testRunner) {
+            testRunner.dumpAsText();
+            testRunner.waitUntilDone();
+        }
+
+        console.log("window.name should equal \"test\" after a cross-origin sub frame navigation.");
+    </script>
+    <iframe src="resources/window-name-test.html?localhost"></iframe>
+</html>
diff --git a/LayoutTests/http/tests/security/window-name-after-same-origin-aux-frame-navigation-expected.txt b/LayoutTests/http/tests/security/window-name-after-same-origin-aux-frame-navigation-expected.txt
new file mode 100644 (file)
index 0000000..7027aa7
--- /dev/null
@@ -0,0 +1,3 @@
+CONSOLE MESSAGE: line 10: window.name should equal "test" after a same-origin auxiliary frame navigation.
+CONSOLE MESSAGE: line 4: window.name equals "test".
+
diff --git a/LayoutTests/http/tests/security/window-name-after-same-origin-aux-frame-navigation.html b/LayoutTests/http/tests/security/window-name-after-same-origin-aux-frame-navigation.html
new file mode 100644 (file)
index 0000000..a41110a
--- /dev/null
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html>
+    <script>
+        if (window.testRunner) {
+            testRunner.dumpAsText();
+            testRunner.waitUntilDone();
+            testRunner.setCanOpenWindows(true);
+        }
+
+        console.log("window.name should equal \"test\" after a same-origin auxiliary frame navigation.");
+        window.open("resources/window-name-test.html?127.0.0.1");
+    </script>
+</html>
diff --git a/LayoutTests/http/tests/security/window-name-after-same-origin-main-frame-navigation-expected.txt b/LayoutTests/http/tests/security/window-name-after-same-origin-main-frame-navigation-expected.txt
new file mode 100644 (file)
index 0000000..a2e31c5
--- /dev/null
@@ -0,0 +1,3 @@
+CONSOLE MESSAGE: line 9: window.name should equal "test" after a same-origin main frame navigation.
+CONSOLE MESSAGE: line 4: window.name equals "test".
+
diff --git a/LayoutTests/http/tests/security/window-name-after-same-origin-main-frame-navigation.html b/LayoutTests/http/tests/security/window-name-after-same-origin-main-frame-navigation.html
new file mode 100644 (file)
index 0000000..5fa7a16
--- /dev/null
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html>
+    <script>
+        if (window.testRunner) {
+            testRunner.dumpAsText();
+            testRunner.waitUntilDone();
+        }
+
+        console.log("window.name should equal \"test\" after a same-origin main frame navigation.");
+        document.location = "resources/window-name-test.html?127.0.0.1";
+    </script>
+</html>
diff --git a/LayoutTests/http/tests/security/window-name-after-same-origin-sub-frame-navigation-expected.txt b/LayoutTests/http/tests/security/window-name-after-same-origin-sub-frame-navigation-expected.txt
new file mode 100644 (file)
index 0000000..7a3148c
--- /dev/null
@@ -0,0 +1,3 @@
+CONSOLE MESSAGE: line 9: window.name should equal "test" after a same-origin sub frame navigation.
+CONSOLE MESSAGE: line 4: window.name equals "test".
+
diff --git a/LayoutTests/http/tests/security/window-name-after-same-origin-sub-frame-navigation.html b/LayoutTests/http/tests/security/window-name-after-same-origin-sub-frame-navigation.html
new file mode 100644 (file)
index 0000000..7737dfc
--- /dev/null
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html>
+    <script>
+        if (window.testRunner) {
+            testRunner.dumpAsText();
+            testRunner.waitUntilDone();
+        }
+
+        console.log("window.name should equal \"test\" after a same-origin sub frame navigation.");
+    </script>
+    <iframe src="resources/window-name-test.html?127.0.0.1"></iframe>
+</html>
index 1e74f0d..77d5075 100644 (file)
@@ -1,3 +1,38 @@
+2016-11-29  Andy Estes  <aestes@apple.com>
+
+        window.name leaks information across domains
+        https://bugs.webkit.org/show_bug.cgi?id=158216
+        <rdar://problem/14548481>
+
+        Reviewed by Brent Fulgham.
+
+        When updating the history after a cross-origin navigation, the HTML Standard says:
+
+        "If the browsing context is a top-level browsing context, but not an auxiliary browsing
+        context, then set the browsing context's name to the empty string."
+
+        https://html.spec.whatwg.org/multipage/browsers.html#resetBCName
+
+        Tests: http/tests/security/window-name-after-cross-origin-aux-frame-navigation.html
+               http/tests/security/window-name-after-cross-origin-main-frame-navigation.html
+               http/tests/security/window-name-after-cross-origin-sub-frame-navigation.html
+               http/tests/security/window-name-after-same-origin-aux-frame-navigation.html
+               http/tests/security/window-name-after-same-origin-main-frame-navigation.html
+               http/tests/security/window-name-after-same-origin-sub-frame-navigation.html
+
+        * loader/FrameLoader.cpp:
+        (WebCore::shouldClearWindowName): Returns true if frame is a main frame with no opener and
+        newDocument does not have the same origin as the frame's current document.
+        (WebCore::FrameLoader::clear): Changed to set m_frame's name to nullAtom if
+        clearWindowProperties and shouldClearWindowName() are true.
+        * page/SecurityOrigin.cpp:
+        (WebCore::SecurityOrigin::canAccessStorage): Changed to call isSameOriginAs() and check
+        m_universalAccess.
+        (WebCore::SecurityOrigin::isSameOriginAs): Renamed from isThirdParty(); removed the check
+        for m_universalAccess.
+        (WebCore::SecurityOrigin::isThirdParty): Renamed to isSameOriginAs().
+        * page/SecurityOrigin.h: Renamed isThirdParty() to isSameOriginAs() and made it public.
+
 2016-11-29  Dave Hyatt  <hyatt@apple.com>
 
         [CSS Parser] Fix parsing of "all" in transitions
index 122a2bd..1443981 100644 (file)
@@ -572,6 +572,17 @@ void FrameLoader::cancelAndClear()
     m_frame.script().updatePlatformScriptObjects();
 }
 
+static inline bool shouldClearWindowName(const Frame& frame, const Document& newDocument)
+{
+    if (!frame.isMainFrame())
+        return false;
+
+    if (frame.loader().opener())
+        return false;
+
+    return !newDocument.securityOrigin()->isSameOriginAs(frame.document()->securityOrigin());
+}
+
 void FrameLoader::clear(Document* newDocument, bool clearWindowProperties, bool clearScriptObjects, bool clearFrameView)
 {
     m_frame.editor().clear();
@@ -594,6 +605,9 @@ void FrameLoader::clear(Document* newDocument, bool clearWindowProperties, bool
         InspectorInstrumentation::frameWindowDiscarded(m_frame, m_frame.document()->domWindow());
         m_frame.document()->domWindow()->resetUnlessSuspendedForDocumentSuspension();
         m_frame.script().clearWindowShell(newDocument->domWindow(), m_frame.document()->pageCacheState() == Document::AboutToEnterPageCache);
+
+        if (shouldClearWindowName(m_frame, *newDocument))
+            m_frame.tree().setName(nullAtom);
     }
 
     m_frame.selection().prepareForDestruction();
index b259ffa..c361792 100644 (file)
@@ -342,7 +342,10 @@ bool SecurityOrigin::canAccessStorage(const SecurityOrigin* topOrigin, ShouldAll
     if (shouldAllowFromThirdParty == AlwaysAllowFromThirdParty)
         return true;
 
-    if ((m_storageBlockingPolicy == BlockThirdPartyStorage || topOrigin->m_storageBlockingPolicy == BlockThirdPartyStorage) && topOrigin->isThirdParty(this))
+    if (m_universalAccess)
+        return true;
+
+    if ((m_storageBlockingPolicy == BlockThirdPartyStorage || topOrigin->m_storageBlockingPolicy == BlockThirdPartyStorage) && !topOrigin->isSameOriginAs(this))
         return false;
 
     return true;
@@ -357,18 +360,15 @@ SecurityOrigin::Policy SecurityOrigin::canShowNotifications() const
     return Ask;
 }
 
-bool SecurityOrigin::isThirdParty(const SecurityOrigin* child) const
+bool SecurityOrigin::isSameOriginAs(const SecurityOrigin* other) const
 {
-    if (child->m_universalAccess)
-        return false;
+    if (this == other)
+        return true;
 
-    if (this == child)
+    if (isUnique() || other->isUnique())
         return false;
 
-    if (isUnique() || child->isUnique())
-        return true;
-
-    return !isSameSchemeHostPort(child);
+    return isSameSchemeHostPort(other);
 }
 
 void SecurityOrigin::grantLoadLocalResources()
index 18cb188..e699bd9 100644 (file)
@@ -196,6 +196,10 @@ public:
     // (and whether it was set) but considering the host. It is used for postMessage.
     WEBCORE_EXPORT bool isSameSchemeHostPort(const SecurityOrigin*) const;
 
+    // This method implements the "same origin" algorithm from the HTML Standard:
+    // https://html.spec.whatwg.org/multipage/browsers.html#same-origin
+    bool isSameOriginAs(const SecurityOrigin*) const;
+
     static URL urlWithUniqueSecurityOrigin();
 
 private:
@@ -205,7 +209,6 @@ private:
 
     // FIXME: Rename this function to something more semantic.
     bool passesFileCheck(const SecurityOrigin*) const;
-    bool isThirdParty(const SecurityOrigin*) const;
 
     // This method checks that the scheme for this origin is an HTTP-family
     // scheme, e.g. HTTP and HTTPS.