WebKit should prevent push/replace state with username in URL.
authorbeidson@apple.com <beidson@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 15 Jul 2016 18:39:27 +0000 (18:39 +0000)
committerbeidson@apple.com <beidson@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 15 Jul 2016 18:39:27 +0000 (18:39 +0000)
<rdar://problem/27361737> and https://bugs.webkit.org/show_bug.cgi?id=159818

Reviewed by Brent Fulgham.

Source/WebCore:

Test: http/tests/security/history-username-password.html

* page/History.cpp:
(WebCore::History::stateObjectAdded): Don't allow URLs with usernames/passwords.

LayoutTests:

* http/tests/security/history-username-password-expected.txt: Added.
* http/tests/security/history-username-password.html: Added.

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

LayoutTests/ChangeLog
LayoutTests/http/tests/security/history-username-password-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/security/history-username-password.html [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/page/History.cpp

index d53883d..f24b844 100644 (file)
@@ -1,3 +1,13 @@
+2016-07-15  Brady Eidson  <beidson@apple.com>
+
+        WebKit should prevent push/replace state with username in URL.
+        <rdar://problem/27361737> and https://bugs.webkit.org/show_bug.cgi?id=159818
+
+        Reviewed by Brent Fulgham.
+
+        * http/tests/security/history-username-password-expected.txt: Added.
+        * http/tests/security/history-username-password.html: Added.
+
 2016-07-15  Ryan Haddad  <ryanhaddad@apple.com>
 
         Unreviewed, rolling out r203266.
diff --git a/LayoutTests/http/tests/security/history-username-password-expected.txt b/LayoutTests/http/tests/security/history-username-password-expected.txt
new file mode 100644 (file)
index 0000000..019724c
--- /dev/null
@@ -0,0 +1,14 @@
+Click to test in new window
+Error: SecurityError: DOM Exception 18
+Error: SecurityError: DOM Exception 18
+Error: SecurityError: DOM Exception 18
+Error: SecurityError: DOM Exception 18
+Error: SecurityError: DOM Exception 18
+Error: SecurityError: DOM Exception 18
+Error: SecurityError: DOM Exception 18
+Error: SecurityError: DOM Exception 18
+Error: SecurityError: DOM Exception 18
+Error: SecurityError: DOM Exception 18
+Error: SecurityError: DOM Exception 18
+Error: SecurityError: DOM Exception 18
+
diff --git a/LayoutTests/http/tests/security/history-username-password.html b/LayoutTests/http/tests/security/history-username-password.html
new file mode 100644 (file)
index 0000000..6331a2c
--- /dev/null
@@ -0,0 +1,82 @@
+<script>
+if (window.testRunner) {
+    testRunner.dumpAsText();
+    testRunner.setCanOpenWindows();
+    testRunner.waitUntilDone();
+}
+
+function log(msg)
+{
+    document.getElementById("logger").innerHTML += msg + "<br>";
+}
+
+function testHistoryObject(historyToTest)
+{
+    try {
+        historyToTest.replaceState(null, "Phishy Title", location.protocol + "//www.webkit.org" + "@" + location.host);
+        log("replaceState with username worked, shouldn't have.");
+    } catch(e) {
+        log(e);
+    }
+
+    try {
+        historyToTest.replaceState(null, "Phishy Title", location.protocol + "//:www.webkit.org" + "@" + location.host);
+        log("replaceState with password worked, shouldn't have.");
+    } catch(e) {
+        log(e);
+    }
+
+    try {
+        historyToTest.replaceState(null, "Phishy Title", location.protocol + "//www.webkit:org" + "@" + location.host);
+        log("replaceState with username and password worked, shouldn't have.");
+    } catch(e) {
+        log(e);
+    }
+
+    try {
+        historyToTest.pushState(null, "Phishy Title", location.protocol + "//www.webkit.org" + "@" + location.host);
+        log("pushState with username worked, shouldn't have.");
+    } catch(e) {
+        log(e);
+    }
+
+    try {
+        historyToTest.pushState(null, "Phishy Title", location.protocol + "//:www.webkit.org" + "@" + location.host);
+        log("pushState with password worked, shouldn't have.");
+    } catch(e) {
+        log(e);
+    }
+
+    try {
+        historyToTest.pushState(null, "Phishy Title", location.protocol + "//www.webkit:org" + "@" + location.host);
+        log("pushState with username and password worked, shouldn't have.");
+    } catch(e) {
+        log(e);
+    }
+}
+
+function clicked()
+{
+    newWindow = window.open('','newWindow');
+    testHistoryObject(newWindow.history);
+    if (window.testRunner)
+        testRunner.notifyDone();
+}
+
+function loaded()
+{
+    testHistoryObject(window.history);
+
+    if (window.eventSender) {
+        var button = document.getElementById("theButton");
+        eventSender.mouseMoveTo(button.offsetLeft + 5, button.offsetTop + 5);
+        eventSender.mouseDown();
+        eventSender.mouseUp();
+    }
+}
+
+</script>
+<body onload="loaded();">
+<button id="theButton" onclick="clicked();">Click to test in new window</button>
+<div id="logger"></div>
+</body>
index 09e0963..8fa1db3 100644 (file)
@@ -1,3 +1,15 @@
+2016-07-15  Brady Eidson  <beidson@apple.com>
+
+        WebKit should prevent push/replace state with username in URL.
+        <rdar://problem/27361737> and https://bugs.webkit.org/show_bug.cgi?id=159818
+
+        Reviewed by Brent Fulgham.
+
+        Test: http/tests/security/history-username-password.html
+
+        * page/History.cpp:
+        (WebCore::History::stateObjectAdded): Don't allow URLs with usernames/passwords.
+
 2016-07-15  Ryan Haddad  <ryanhaddad@apple.com>
 
         Unreviewed, rolling out r203266.
index 292763f..6859d7e 100644 (file)
@@ -152,6 +152,15 @@ void History::stateObjectAdded(PassRefPtr<SerializedScriptValue> data, const Str
         return;
     }
 
+    if (fullURL.hasUsername() || fullURL.hasPassword()) {
+        ec.code = SECURITY_ERR;
+        if (stateObjectType == StateObjectType::Replace)
+            ec.message = makeString("Attempt to use history.replaceState() to change session history URL to ", fullURL.string(), " is insecure; Username/passwords aren't allowed in state object URLs");
+        else
+            ec.message = makeString("Attempt to use history.pushState() to add URL ", fullURL.string(), " to session history is insecure; Username/passwords aren't allowed in state object URLs");
+        return;
+    }
+
     Document* mainDocument = m_frame->page()->mainFrame().document();
     History* mainHistory = nullptr;
     if (mainDocument) {