Restrict security origin inheritance to empty, about:blank, and about:srcdoc URLs
authorwilander@apple.com <wilander@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 17 Jun 2016 01:10:08 +0000 (01:10 +0000)
committerwilander@apple.com <wilander@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 17 Jun 2016 01:10:08 +0000 (01:10 +0000)
https://bugs.webkit.org/show_bug.cgi?id=158855
<rdar://problem/26142632>

Reviewed by Alex Christensen.

Source/WebCore:

Tests: http/tests/dom/window-open-about-blank-and-access-document.html
       http/tests/dom/window-open-about-webkit-org-and-access-document.html

Document.cpp previously checked whether a document should inherit its owner's
security origin by checking if the URL is either empty or blank. URL.cpp in
turn only checks if the protocol is "about:" in the isBlankURL() function.
Thus all about:* URLs inherited security origin. This patch restricts
security origin inheritance to empty, about:blank, and about:srcdoc URLs.

Quotes and links from the WHATWG spec regarding about:srcdoc:

7.1 Browsing contexts
A browsing context can have a creator browsing context, the browsing context
that was responsible for its creation. If a browsing context has a parent
browsing context, then that is its creator browsing context. Otherwise, if the
browsing context has an opener browsing context, then that is its creator
browsing context. Otherwise, the browsing context has no creator browsing
context.
https://html.spec.whatwg.org/multipage/browsers.html#concept-document-bc

7.1.1 Nested browsing contexts
Certain elements (for example, iframe elements) can instantiate further
browsing contexts. These are called nested browsing contexts. If a browsing
context P has a Document D with an element E that nests another browsing
context C inside it, then C is said to be nested through D, and E is said to
be the browsing context container of C. If the browsing context container
element E is in the Document D, then P is said to be the parent browsing
context of C and C is said to be a child browsing context of P. Otherwise,
the nested browsing context C has no parent browsing context.
https://html.spec.whatwg.org/multipage/browsers.html#nested-browsing-context

4.8.5 The iframe element
The iframe element represents a nested browsing context.
...
If the srcdoc attribute is specified
    Navigate the element's child browsing context to a new response whose
    url list consists of about:srcdoc ...
https://html.spec.whatwg.org/multipage/embedded-content.html#attr-iframe-srcdoc

* dom/Document.cpp:
(WebCore::Document::initSecurityContext):
    Now uses the URL::shouldInheritSecurityOriginFromOwner() function instead.
(WebCore::Document::initContentSecurityPolicy):
    Now uses the URL::shouldInheritSecurityOriginFromOwner() function instead.
(WebCore::shouldInheritSecurityOriginFromOwner): Deleted.
    Moved to URL::shouldInheritSecurityOriginFromOwner() and restricted the check.
* platform/URL.cpp:
(WebCore::URL::shouldInheritSecurityOriginFromOwner):
* platform/URL.h:
    Moved the function from Document and restricted the check to only allow
    security origin inheritance for empty, about:blank, and about:srcdoc URLs.

LayoutTests:

* http/tests/dom/window-open-about-blank-and-access-document-expected.txt: Added.
* http/tests/dom/window-open-about-blank-and-access-document.html: Added.
* http/tests/dom/window-open-about-webkit-org-and-access-document-expected.txt: Added.
* http/tests/dom/window-open-about-webkit-org-and-access-document.html: Added.

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

LayoutTests/ChangeLog
LayoutTests/http/tests/dom/window-open-about-blank-and-access-document-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/dom/window-open-about-blank-and-access-document.html [new file with mode: 0644]
LayoutTests/http/tests/dom/window-open-about-webkit-org-and-access-document-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/dom/window-open-about-webkit-org-and-access-document.html [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/dom/Document.cpp
Source/WebCore/platform/URL.cpp
Source/WebCore/platform/URL.h

index 4def98382d6ac248a8daeb9f019f10e76ffd735b..9edc60ca3638c64de8673d38ceb8872ee6a50021 100644 (file)
@@ -1,3 +1,16 @@
+2016-06-16  John Wilander  <wilander@apple.com>
+
+        Restrict security origin inheritance to empty, about:blank, and about:srcdoc URLs
+        https://bugs.webkit.org/show_bug.cgi?id=158855
+        <rdar://problem/26142632>
+
+        Reviewed by Alex Christensen.
+
+        * http/tests/dom/window-open-about-blank-and-access-document-expected.txt: Added.
+        * http/tests/dom/window-open-about-blank-and-access-document.html: Added.
+        * http/tests/dom/window-open-about-webkit-org-and-access-document-expected.txt: Added.
+        * http/tests/dom/window-open-about-webkit-org-and-access-document.html: Added.
+
 2016-06-16  Jiewen Tan  <jiewen_tan@apple.com>
 
         Move most of CSP tests into security/contentSecurityPolicy/
 2016-06-16  Jiewen Tan  <jiewen_tan@apple.com>
 
         Move most of CSP tests into security/contentSecurityPolicy/
diff --git a/LayoutTests/http/tests/dom/window-open-about-blank-and-access-document-expected.txt b/LayoutTests/http/tests/dom/window-open-about-blank-and-access-document-expected.txt
new file mode 100644 (file)
index 0000000..c859c48
--- /dev/null
@@ -0,0 +1,4 @@
+CONSOLE MESSAGE: line 1: Injected script running.
+
+PASS newWindow.document is defined.
+
diff --git a/LayoutTests/http/tests/dom/window-open-about-blank-and-access-document.html b/LayoutTests/http/tests/dom/window-open-about-blank-and-access-document.html
new file mode 100644 (file)
index 0000000..6a5f6ce
--- /dev/null
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <meta charset="UTF-8">
+    <title>Tests opening a new about:blank window and accessing its document</title>
+    <script src="../resources/js-test-pre.js"></script>
+    <script>
+        var newWindow;
+
+        if (window.testRunner) {
+            testRunner.setCanOpenWindows();
+            testRunner.waitUntilDone();
+        }
+
+        function checkNewWindowDocumentIsUndefined () {
+            shouldBeDefined("newWindow.document");
+            if (window.testRunner)
+                testRunner.notifyDone();
+        }
+
+        function clickHandler() {
+            newWindow = window.open("about:blank");
+            try {
+                newWindow.document.write("<scri" + "pt>console.log('Injected script running.')</sc" + "ript>");
+                setTimeout(checkNewWindowDocumentIsUndefined, 500);
+            } catch (e) {
+                testFailed("Was not able to write to the new window's document.");
+                if (window.testRunner)
+                    testRunner.notifyDone();
+            }
+        }
+
+        function clickButton() {
+            var button = document.getElementById("test");
+            var buttonX = button.offsetLeft + button.offsetWidth / 2;
+            var buttonY = button.offsetTop + button.offsetHeight / 2;
+            if (window.eventSender) {
+                eventSender.mouseMoveTo(buttonX, buttonY);
+                eventSender.mouseDown();
+                eventSender.mouseUp();
+            }
+        }
+    </script>
+</head>
+<body onload="clickButton()">
+<button id="test" onclick="clickHandler()"></button>
+<div id="console"></div>
+</body>
+</html>
\ No newline at end of file
diff --git a/LayoutTests/http/tests/dom/window-open-about-webkit-org-and-access-document-expected.txt b/LayoutTests/http/tests/dom/window-open-about-webkit-org-and-access-document-expected.txt
new file mode 100644 (file)
index 0000000..d982dbc
--- /dev/null
@@ -0,0 +1,7 @@
+CONSOLE MESSAGE: line 45: Blocked a frame with origin "http://127.0.0.1:8000" from accessing a frame with origin "null".  The frame requesting access has a protocol of "http", the frame being accessed has a protocol of "about". Protocols must match.
+
+CONSOLE MESSAGE: line 347: Blocked a frame with origin "http://127.0.0.1:8000" from accessing a frame with origin "null".  The frame requesting access has a protocol of "http", the frame being accessed has a protocol of "about". Protocols must match.
+
+
+PASS newWindow.document is undefined.
+
diff --git a/LayoutTests/http/tests/dom/window-open-about-webkit-org-and-access-document.html b/LayoutTests/http/tests/dom/window-open-about-webkit-org-and-access-document.html
new file mode 100644 (file)
index 0000000..6033281
--- /dev/null
@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <title>Tests opening a new about://webkit.org window and accessing its document</title>
+    <script src="../resources/js-test-pre.js"></script>
+    <script>
+        var newWindow;
+
+        if (window.testRunner) {
+            testRunner.setCanOpenWindows();
+            testRunner.waitUntilDone();
+        }
+
+        function checkNewWindowDocumentIsUndefined () {
+            shouldBeUndefined("newWindow.document");
+            if (window.testRunner)
+                testRunner.notifyDone();
+        }
+
+        function clickHandler() {
+            newWindow = window.open("about://webkit.org");
+            try {
+                newWindow.document.write("<scri" + "pt>console.log('Injected script running.')</sc" + "ript>");
+                testFailed("Was able to write to the new window's document.");
+                if (window.testRunner)
+                    testRunner.notifyDone();
+            } catch (e) {
+                setTimeout(checkNewWindowDocumentIsUndefined, 500);
+            }
+        }
+
+        function clickButton() {
+            var button = document.getElementById("test");
+            var buttonX = button.offsetLeft + button.offsetWidth / 2;
+            var buttonY = button.offsetTop + button.offsetHeight / 2;
+            if (window.eventSender) {
+                eventSender.mouseMoveTo(buttonX, buttonY);
+                eventSender.mouseDown();
+                eventSender.mouseUp();
+            }
+        }
+    </script>
+</head>
+<body onload="clickButton()">
+<button id="test" onclick="clickHandler()"></button>
+<div id="console"></div>
+</body>
+</html>
\ No newline at end of file
index 1893c91a0074e6e9d09a6f0bd38665ccc4db2dd8..a6aedee6876110e69355261f9c6834059767ac2b 100644 (file)
@@ -1,3 +1,63 @@
+2016-06-16  John Wilander  <wilander@apple.com>
+
+        Restrict security origin inheritance to empty, about:blank, and about:srcdoc URLs
+        https://bugs.webkit.org/show_bug.cgi?id=158855
+        <rdar://problem/26142632>
+
+        Reviewed by Alex Christensen.
+
+        Tests: http/tests/dom/window-open-about-blank-and-access-document.html
+               http/tests/dom/window-open-about-webkit-org-and-access-document.html
+
+        Document.cpp previously checked whether a document should inherit its owner's 
+        security origin by checking if the URL is either empty or blank. URL.cpp in 
+        turn only checks if the protocol is "about:" in the isBlankURL() function. 
+        Thus all about:* URLs inherited security origin. This patch restricts 
+        security origin inheritance to empty, about:blank, and about:srcdoc URLs.
+
+        Quotes and links from the WHATWG spec regarding about:srcdoc:
+
+        7.1 Browsing contexts
+        A browsing context can have a creator browsing context, the browsing context 
+        that was responsible for its creation. If a browsing context has a parent 
+        browsing context, then that is its creator browsing context. Otherwise, if the 
+        browsing context has an opener browsing context, then that is its creator 
+        browsing context. Otherwise, the browsing context has no creator browsing 
+        context.
+        https://html.spec.whatwg.org/multipage/browsers.html#concept-document-bc
+
+        7.1.1 Nested browsing contexts
+        Certain elements (for example, iframe elements) can instantiate further 
+        browsing contexts. These are called nested browsing contexts. If a browsing 
+        context P has a Document D with an element E that nests another browsing 
+        context C inside it, then C is said to be nested through D, and E is said to 
+        be the browsing context container of C. If the browsing context container 
+        element E is in the Document D, then P is said to be the parent browsing 
+        context of C and C is said to be a child browsing context of P. Otherwise, 
+        the nested browsing context C has no parent browsing context.
+        https://html.spec.whatwg.org/multipage/browsers.html#nested-browsing-context
+
+        4.8.5 The iframe element
+        The iframe element represents a nested browsing context.
+        ...
+        If the srcdoc attribute is specified
+            Navigate the element's child browsing context to a new response whose 
+            url list consists of about:srcdoc ...
+        https://html.spec.whatwg.org/multipage/embedded-content.html#attr-iframe-srcdoc
+
+        * dom/Document.cpp:
+        (WebCore::Document::initSecurityContext):
+            Now uses the URL::shouldInheritSecurityOriginFromOwner() function instead.
+        (WebCore::Document::initContentSecurityPolicy):
+            Now uses the URL::shouldInheritSecurityOriginFromOwner() function instead.
+        (WebCore::shouldInheritSecurityOriginFromOwner): Deleted.
+            Moved to URL::shouldInheritSecurityOriginFromOwner() and restricted the check.
+        * platform/URL.cpp:
+        (WebCore::URL::shouldInheritSecurityOriginFromOwner):
+        * platform/URL.h:
+            Moved the function from Document and restricted the check to only allow
+            security origin inheritance for empty, about:blank, and about:srcdoc URLs.
+
 2016-06-16  Simon Fraser  <simon.fraser@apple.com>
 
         [iOS] Focus event dispatched in iframe causes parent document to scroll incorrectly
 2016-06-16  Simon Fraser  <simon.fraser@apple.com>
 
         [iOS] Focus event dispatched in iframe causes parent document to scroll incorrectly
index d6ed6d809d39b9aea0d5413b143ab81045a18c36..e763a294dce949190d79371e990496a113ce9892 100644 (file)
@@ -349,19 +349,6 @@ static inline bool isValidNamePart(UChar32 c)
     return true;
 }
 
     return true;
 }
 
-static bool shouldInheritSecurityOriginFromOwner(const URL& url)
-{
-    // http://www.whatwg.org/specs/web-apps/current-work/#origin-0
-    //
-    // If a Document has the address "about:blank"
-    //     The origin of the Document is the origin it was assigned when its browsing context was created.
-    //
-    // Note: We generalize this to all "blank" URLs and invalid URLs because we
-    // treat all of these URLs as about:blank.
-    //
-    return url.isEmpty() || url.isBlankURL();
-}
-
 static Widget* widgetForElement(Element* focusedElement)
 {
     if (!focusedElement)
 static Widget* widgetForElement(Element* focusedElement)
 {
     if (!focusedElement)
@@ -5273,7 +5260,7 @@ void Document::initSecurityContext()
         setBaseURLOverride(parentDocument->baseURL());
     }
 
         setBaseURLOverride(parentDocument->baseURL());
     }
 
-    if (!shouldInheritSecurityOriginFromOwner(m_url))
+    if (!m_url.shouldInheritSecurityOriginFromOwner())
         return;
 
     // If we do not obtain a meaningful origin from the URL, then we try to
         return;
 
     // If we do not obtain a meaningful origin from the URL, then we try to
@@ -5316,7 +5303,7 @@ void Document::initSecurityContext()
 
 void Document::initContentSecurityPolicy()
 {
 
 void Document::initContentSecurityPolicy()
 {
-    if (!m_frame->tree().parent() || (!shouldInheritSecurityOriginFromOwner(m_url) && !isPluginDocument()))
+    if (!m_frame->tree().parent() || (!m_url.shouldInheritSecurityOriginFromOwner() && !isPluginDocument()))
         return;
 
     contentSecurityPolicy()->copyStateFrom(m_frame->tree().parent()->document()->contentSecurityPolicy());
         return;
 
     contentSecurityPolicy()->copyStateFrom(m_frame->tree().parent()->document()->contentSecurityPolicy());
index f3725bcef0450cf1f06e756c31b398718980440d..9b6c29bc2504ec3c11d01828034fd92f3c6e459c 100644 (file)
@@ -2062,6 +2062,13 @@ bool URL::isBlankURL() const
     return protocolIs("about");
 }
 
     return protocolIs("about");
 }
 
+bool URL::shouldInheritSecurityOriginFromOwner() const
+{
+    return isEmpty()
+        || m_string == blankURL().string()
+        || m_string == "about:srcdoc";
+}
+
 typedef HashMap<String, unsigned short, ASCIICaseInsensitiveHash> DefaultPortsMap;
 static const DefaultPortsMap& defaultPortsMap()
 {
 typedef HashMap<String, unsigned short, ASCIICaseInsensitiveHash> DefaultPortsMap;
 static const DefaultPortsMap& defaultPortsMap()
 {
index e6e2ba6bf3741ffa45a001b78e1a39bff41e1793..f77e2cc6ee53d9f931f15b39c4c5ecf680ed656d 100644 (file)
@@ -131,6 +131,7 @@ public:
     bool protocolIsInHTTPFamily() const;
     WEBCORE_EXPORT bool isLocalFile() const;
     bool isBlankURL() const;
     bool protocolIsInHTTPFamily() const;
     WEBCORE_EXPORT bool isLocalFile() const;
     bool isBlankURL() const;
+    bool shouldInheritSecurityOriginFromOwner() const;
 
     WEBCORE_EXPORT bool setProtocol(const String&);
     void setHost(const String&);
 
     WEBCORE_EXPORT bool setProtocol(const String&);
     void setHost(const String&);