Treat non-https actions on secure pages as mixed content
authorweinig@apple.com <weinig@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 22 Jan 2016 22:24:32 +0000 (22:24 +0000)
committerweinig@apple.com <weinig@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 22 Jan 2016 22:24:32 +0000 (22:24 +0000)
<rdar://problem/23144492>
https://bugs.webkit.org/show_bug.cgi?id=153322
Source/WebCore:

Reviewed by Alexey Proskuryakov.

Tests:  http/tests/security/mixedContent/insecure-form-in-iframe.html
        http/tests/security/mixedContent/insecure-form-in-main-frame.html
        http/tests/security/mixedContent/javascript-url-form-in-main-frame.html

* html/HTMLFormElement.cpp:
(WebCore::HTMLFormElement::parseAttribute):
Check form actions for mixed content.

* loader/MixedContentChecker.cpp:
(WebCore::MixedContentChecker::checkFormForMixedContent):
* loader/MixedContentChecker.h:
Add new function to check and warn if a form's action is mixed content.

LayoutTests:

Reviewed by Alexey Proskuryakov.

* http/tests/security/mixedContent/insecure-form-in-iframe-expected.txt: Added.
* http/tests/security/mixedContent/insecure-form-in-iframe.html: Added.
* http/tests/security/mixedContent/insecure-form-in-main-frame-expected.txt: Added.
* http/tests/security/mixedContent/insecure-form-in-main-frame.html: Added.
* http/tests/security/mixedContent/javascript-url-form-in-main-frame-expected.txt: Added.
* http/tests/security/mixedContent/javascript-url-form-in-main-frame.html: Added.
* http/tests/security/mixedContent/resources/frame-with-insecure-form.html: Added.
* http/tests/security/mixedContent/resources/frame-with-javascript-url-form.html: Added.

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

14 files changed:
LayoutTests/ChangeLog
LayoutTests/http/tests/security/mixedContent/insecure-form-in-iframe-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/security/mixedContent/insecure-form-in-iframe.html [new file with mode: 0644]
LayoutTests/http/tests/security/mixedContent/insecure-form-in-main-frame-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/security/mixedContent/insecure-form-in-main-frame.html [new file with mode: 0644]
LayoutTests/http/tests/security/mixedContent/javascript-url-form-in-main-frame-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/security/mixedContent/javascript-url-form-in-main-frame.html [new file with mode: 0644]
LayoutTests/http/tests/security/mixedContent/resources/frame-with-insecure-form.html [new file with mode: 0644]
LayoutTests/http/tests/security/mixedContent/resources/frame-with-javascript-url-form.html [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/html/HTMLFormElement.cpp
Source/WebCore/loader/MixedContentChecker.cpp
Source/WebCore/loader/MixedContentChecker.h
Tools/WebEditingTester/WK2WebDocumentController.m

index 280edcb..f1779a4 100644 (file)
@@ -1,3 +1,20 @@
+2016-01-21  Sam Weinig  <sam@webkit.org>
+
+        Treat non-https actions on secure pages as mixed content
+        <rdar://problem/23144492>
+        https://bugs.webkit.org/show_bug.cgi?id=153322
+        
+        Reviewed by Alexey Proskuryakov.
+
+        * http/tests/security/mixedContent/insecure-form-in-iframe-expected.txt: Added.
+        * http/tests/security/mixedContent/insecure-form-in-iframe.html: Added.
+        * http/tests/security/mixedContent/insecure-form-in-main-frame-expected.txt: Added.
+        * http/tests/security/mixedContent/insecure-form-in-main-frame.html: Added.
+        * http/tests/security/mixedContent/javascript-url-form-in-main-frame-expected.txt: Added.
+        * http/tests/security/mixedContent/javascript-url-form-in-main-frame.html: Added.
+        * http/tests/security/mixedContent/resources/frame-with-insecure-form.html: Added.
+        * http/tests/security/mixedContent/resources/frame-with-javascript-url-form.html: Added.
+
 2016-01-22  Ryan Haddad  <ryanhaddad@apple.com>
 
         Marking imported/w3c/web-platform-tests/XMLHttpRequest/getresponseheader-chunked-trailer.htm as flaky on ios-simulator
diff --git a/LayoutTests/http/tests/security/mixedContent/insecure-form-in-iframe-expected.txt b/LayoutTests/http/tests/security/mixedContent/insecure-form-in-iframe-expected.txt
new file mode 100644 (file)
index 0000000..6e7bd91
--- /dev/null
@@ -0,0 +1,11 @@
+frame "<!--framePath //<!--frame0-->-->" - didStartProvisionalLoadForFrame
+main frame - didFinishDocumentLoadForFrame
+frame "<!--framePath //<!--frame0-->-->" - didCommitLoadForFrame
+frame "<!--framePath //<!--frame0-->-->" - didFinishDocumentLoadForFrame
+frame "<!--framePath //<!--frame0-->-->" - didHandleOnloadEventsForFrame
+main frame - didHandleOnloadEventsForFrame
+frame "<!--framePath //<!--frame0-->-->" - didFinishLoadForFrame
+main frame - didFinishLoadForFrame
+This test loads a secure iframe that has a form with an insecure action. We should *not* get a mixed content callback because the main frame is HTTP and the form doesn't contaminate the child iframe's security origin with mixed content.
+
+
diff --git a/LayoutTests/http/tests/security/mixedContent/insecure-form-in-iframe.html b/LayoutTests/http/tests/security/mixedContent/insecure-form-in-iframe.html
new file mode 100644 (file)
index 0000000..cb9c4fd
--- /dev/null
@@ -0,0 +1,14 @@
+<html>
+<body>
+<script>
+if (window.testRunner) {
+    testRunner.dumpAsText();
+    testRunner.dumpFrameLoadCallbacks();
+}
+</script>
+<p>This test loads a secure iframe that has a form with an insecure action.  We should
+*not* get a mixed content callback because the main frame is HTTP and the form
+doesn't contaminate the child iframe's security origin with mixed content.</p>
+<iframe src="https://127.0.0.1:8443/security/mixedContent/resources/frame-with-insecure-form.html"></iframe>
+</body>
+</html>
diff --git a/LayoutTests/http/tests/security/mixedContent/insecure-form-in-main-frame-expected.txt b/LayoutTests/http/tests/security/mixedContent/insecure-form-in-main-frame-expected.txt
new file mode 100644 (file)
index 0000000..6f00814
--- /dev/null
@@ -0,0 +1,12 @@
+main frame - didFinishDocumentLoadForFrame
+main frame - didStartProvisionalLoadForFrame
+main frame - didHandleOnloadEventsForFrame
+main frame - didFinishLoadForFrame
+main frame - didCommitLoadForFrame
+CONSOLE MESSAGE: line 3: The page at https://127.0.0.1:8443/security/mixedContent/resources/frame-with-insecure-form.html contains a form which targets an insecure URL http://127.0.0.1:8080/resources/doesnotexist.
+
+didDisplayInsecureContent
+main frame - didFinishDocumentLoadForFrame
+main frame - didHandleOnloadEventsForFrame
+main frame - didFinishLoadForFrame
+This test opens a window that has a form with an non-https action. We should trigger a mixed content callback because the main frame in the window is HTTPS but is running insecure content.
diff --git a/LayoutTests/http/tests/security/mixedContent/insecure-form-in-main-frame.html b/LayoutTests/http/tests/security/mixedContent/insecure-form-in-main-frame.html
new file mode 100644 (file)
index 0000000..057d5d0
--- /dev/null
@@ -0,0 +1,27 @@
+<html>
+<body>
+<script>
+if (window.testRunner) {
+    testRunner.waitUntilDone();
+    testRunner.dumpAsText();
+    testRunner.dumpFrameLoadCallbacks();
+    testRunner.setCanOpenWindows();
+    testRunner.setCloseRemainingWindowsWhenComplete(true);
+}
+
+window.addEventListener("message", function (e) {
+  if (window.testRunner)
+      testRunner.notifyDone();
+}, false);
+
+</script>
+<p>This test opens a window that has a form with an non-https action.  We should
+trigger a mixed content callback because the main frame in the window is HTTPS
+but is running insecure content.</p>
+<script>
+onload = function() {
+    window.open("https://127.0.0.1:8443/security/mixedContent/resources/frame-with-insecure-form.html");
+}
+</script>
+</body>
+</html>
diff --git a/LayoutTests/http/tests/security/mixedContent/javascript-url-form-in-main-frame-expected.txt b/LayoutTests/http/tests/security/mixedContent/javascript-url-form-in-main-frame-expected.txt
new file mode 100644 (file)
index 0000000..16fefac
--- /dev/null
@@ -0,0 +1,9 @@
+main frame - didFinishDocumentLoadForFrame
+main frame - didStartProvisionalLoadForFrame
+main frame - didHandleOnloadEventsForFrame
+main frame - didFinishLoadForFrame
+main frame - didCommitLoadForFrame
+main frame - didFinishDocumentLoadForFrame
+main frame - didHandleOnloadEventsForFrame
+main frame - didFinishLoadForFrame
+This test opens a window that has a form with an action that is a javascript: url. We should *not* trigger a mixed content callback because the javascript: URL cannot be corrupted by active network attackers.
diff --git a/LayoutTests/http/tests/security/mixedContent/javascript-url-form-in-main-frame.html b/LayoutTests/http/tests/security/mixedContent/javascript-url-form-in-main-frame.html
new file mode 100644 (file)
index 0000000..48ec3b1
--- /dev/null
@@ -0,0 +1,27 @@
+<html>
+<body>
+<script>
+if (window.testRunner) {
+    testRunner.waitUntilDone();
+    testRunner.dumpAsText();
+    testRunner.dumpFrameLoadCallbacks();
+    testRunner.setCanOpenWindows();
+    testRunner.setCloseRemainingWindowsWhenComplete(true);
+}
+
+window.addEventListener("message", function (e) {
+  if (window.testRunner)
+      testRunner.notifyDone();
+}, false);
+
+</script>
+<p>This test opens a window that has a form with an action that is a javascript: url.  We should
+*not* trigger a mixed content callback because the javascript: URL cannot be corrupted
+by active network attackers.</p>
+<script>
+onload = function() {
+    window.open("https://127.0.0.1:8443/security/mixedContent/resources/frame-with-javascript-url-form.html");
+}
+</script>
+</body>
+</html>
diff --git a/LayoutTests/http/tests/security/mixedContent/resources/frame-with-insecure-form.html b/LayoutTests/http/tests/security/mixedContent/resources/frame-with-insecure-form.html
new file mode 100644 (file)
index 0000000..dcd6e5d
--- /dev/null
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<body onload="loaded()">
+<form action="http://127.0.0.1:8080/resources/doesnotexist"></form>
+<script>
+function loaded() {
+    if (window.opener)
+        window.opener.postMessage('done', '*');
+}
+</script>
+</body>
diff --git a/LayoutTests/http/tests/security/mixedContent/resources/frame-with-javascript-url-form.html b/LayoutTests/http/tests/security/mixedContent/resources/frame-with-javascript-url-form.html
new file mode 100644 (file)
index 0000000..ef65e8f
--- /dev/null
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<body onload="loaded()">
+<form action="javascript:void()"></form>
+<script>
+function loaded() {
+    if (window.opener)
+        window.opener.postMessage('done', '*');
+}
+</script>
+</body>
index d11cb62..348778c 100644 (file)
@@ -1,3 +1,24 @@
+2016-01-21  Sam Weinig  <sam@webkit.org>
+
+        Treat non-https actions on secure pages as mixed content
+        <rdar://problem/23144492>
+        https://bugs.webkit.org/show_bug.cgi?id=153322
+
+        Reviewed by Alexey Proskuryakov.
+
+        Tests:  http/tests/security/mixedContent/insecure-form-in-iframe.html
+                http/tests/security/mixedContent/insecure-form-in-main-frame.html
+                http/tests/security/mixedContent/javascript-url-form-in-main-frame.html
+
+        * html/HTMLFormElement.cpp:
+        (WebCore::HTMLFormElement::parseAttribute):
+        Check form actions for mixed content.
+
+        * loader/MixedContentChecker.cpp:
+        (WebCore::MixedContentChecker::checkFormForMixedContent):
+        * loader/MixedContentChecker.h:
+        Add new function to check and warn if a form's action is mixed content.
+
 2016-01-22  Nan Wang  <n_wang@apple.com>
 
         AX: Crash in setTextMarkerDataWithCharacterOffset
index 80a28b4..686576d 100644 (file)
@@ -481,9 +481,16 @@ void HTMLFormElement::requestAutocompleteTimerFired()
 
 void HTMLFormElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
 {
-    if (name == actionAttr)
+    if (name == actionAttr) {
         m_attributes.parseAction(value);
-    else if (name == targetAttr)
+        
+        if (!m_attributes.action().isEmpty()) {
+            if (Frame* f = document().frame()) {
+                Frame& topFrame = f->tree().top();
+                topFrame.loader().mixedContentChecker().checkFormForMixedContent(topFrame.document()->securityOrigin(), document().completeURL(m_attributes.action()));
+            }
+        }
+    } else if (name == targetAttr)
         m_attributes.setTarget(value);
     else if (name == methodAttr)
         m_attributes.updateMethodType(value);
@@ -496,8 +503,7 @@ void HTMLFormElement::parseAttribute(const QualifiedName& name, const AtomicStri
             document().registerForDocumentSuspensionCallbacks(this);
         else
             document().unregisterForDocumentSuspensionCallbacks(this);
-    }
-    else
+    } else
         HTMLElement::parseAttribute(name, value);
 }
 
index a7c9d37..3656cb1 100644 (file)
@@ -88,6 +88,22 @@ bool MixedContentChecker::canRunInsecureContent(SecurityOrigin* securityOrigin,
     return allowed;
 }
 
+void MixedContentChecker::checkFormForMixedContent(SecurityOrigin* securityOrigin, const URL& url) const
+{
+    // Unconditionally allow javascript: URLs as form actions as some pages do this and it does not introduce
+    // a mixed content issue.
+    if (protocolIsJavaScript(url))
+        return;
+
+    if (!isMixedContent(securityOrigin, url))
+        return;
+
+    String message = makeString("The page at ", m_frame.document()->url().stringCenterEllipsizedToLength(), " contains a form which targets an insecure URL ", url.stringCenterEllipsizedToLength(), ".\n");
+    m_frame.document()->addConsoleMessage(MessageSource::Security, MessageLevel::Warning, message);
+
+    client().didDisplayInsecureContent();
+}
+
 void MixedContentChecker::logWarning(bool allowed, const String& action, const URL& target) const
 {
     const char* errorString = allowed ? " was allowed to " : " was not allowed to ";
index f097b5b..66de326 100644 (file)
@@ -53,6 +53,7 @@ public:
 
     bool canDisplayInsecureContent(SecurityOrigin*, ContentType, const URL&) const;
     bool canRunInsecureContent(SecurityOrigin*, const URL&) const;
+    void checkFormForMixedContent(SecurityOrigin*, const URL&) const;
     static bool isMixedContent(SecurityOrigin*, const URL&);
 
 private:
index 49911d8..a69dba6 100644 (file)
@@ -81,7 +81,13 @@ static WKWebViewConfiguration *defaultConfiguration()
 
 - (void)loadHTMLString:(NSString *)content
 {
-    [_webView loadHTMLString:content baseURL:nil];
+    NSStringEncoding encoding = NSUnicodeStringEncoding;
+
+    NSData *data = [content dataUsingEncoding:encoding];
+    CFStringEncoding cfEncoding = CFStringConvertNSStringEncodingToEncoding(encoding);
+    NSString *textEncodingName = (__bridge NSString *)CFStringConvertEncodingToIANACharSetName(cfEncoding);
+
+    [_webView _loadData:data MIMEType:@"text/html" characterEncodingName:textEncodingName baseURL:[NSURL URLWithString:@"x-webdoc:/klsadfgjlfsdj/"] userData:nil];
 }
 
 - (void)performTextFinderAction:(id)sender