Implement the form-action Content Security Policy directive.
authorcommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 16 Aug 2012 12:42:09 +0000 (12:42 +0000)
committercommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 16 Aug 2012 12:42:09 +0000 (12:42 +0000)
https://bugs.webkit.org/show_bug.cgi?id=93777

Patch by Mike West <mkwst@chromium.org> on 2012-08-16
Reviewed by Jochen Eisinger.

Source/WebCore:

The CSP 1.1 editor's draft defines the 'form-action' directive as a
mechanism for whitelisting valid targets for form submission from a
protected resource. A web author might desire to restrict form
submissions to the same origin as the protected resource itself via
a Content Security Policy of "form-action 'self'", or ensure that all
submissions were sent over an SSL connection via "form-action https:".

Specification details available at: https://dvcs.w3.org/hg/content-security-policy/raw-file/tip/csp-specification.dev.html#form-action--experimental

This experimental directive is gated on the ENABLE_CSP_NEXT flag, which
is currently only enabled in Chromium.

Tests: http/tests/security/contentSecurityPolicy/1.1/form-action-src-allowed.html
       http/tests/security/contentSecurityPolicy/1.1/form-action-src-blocked.html
       http/tests/security/contentSecurityPolicy/1.1/form-action-src-default-ignored.html
       http/tests/security/contentSecurityPolicy/1.1/form-action-src-get-allowed.html
       http/tests/security/contentSecurityPolicy/1.1/form-action-src-get-blocked.html
       http/tests/security/contentSecurityPolicy/1.1/form-action-src-javascript-blocked.html
       http/tests/security/contentSecurityPolicy/1.1/form-action-src-redirect-blocked.html

* loader/FrameLoader.cpp:
(WebCore::FrameLoader::checkIfFormActionAllowedByCSP):
    Adding a callback to FrameLoader in order to allow the
    MainResourceLoader to check the relevant CSP status without knowing
    anything about CSP.
(WebCore):
* loader/FrameLoader.h:
(FrameLoader):
* loader/MainResourceLoader.cpp:
(WebCore::MainResourceLoader::willSendRequest):
    Check against the protected resource's Content Security Policy when
    presented with a request that is itself a form submission, or is the
    result of a redirect in response to a form submission. If CSP would
    block the target, cancel the request.
* page/ContentSecurityPolicy.cpp:
(CSPDirectiveList):
(WebCore::CSPDirectiveList::checkSourceAndReportViolation):
    Added explanatory text to the source violation console warning that
    specifically calls out sending form data (as opposed to "connect to"
    or "load the").
(WebCore::CSPDirectiveList::allowFormAction):
    Check a URL against a directive list's the 'form-action' source list.
(WebCore):
(WebCore::CSPDirectiveList::addDirective):
    Recognize the 'form-action' CSP directive.
(WebCore::ContentSecurityPolicy::allowFormAction):
    Public interface to check a form action.
* page/ContentSecurityPolicy.h:

LayoutTests:

* http/tests/navigation/resources/redirection-response.php:
    Allow for the specification of alternate hosts for the redirect. We
    need this capability in order to check that an initial submission
    followed by a blocked redirect results in the correct behavior.
* http/tests/security/contentSecurityPolicy/1.1/form-action-src-allowed-expected.txt: Added.
* http/tests/security/contentSecurityPolicy/1.1/form-action-src-allowed.html: Added.
* http/tests/security/contentSecurityPolicy/1.1/form-action-src-blocked-expected.txt: Added.
* http/tests/security/contentSecurityPolicy/1.1/form-action-src-blocked.html: Added.
* http/tests/security/contentSecurityPolicy/1.1/form-action-src-default-ignored-expected.txt: Added.
* http/tests/security/contentSecurityPolicy/1.1/form-action-src-default-ignored.html: Added.
* http/tests/security/contentSecurityPolicy/1.1/form-action-src-get-allowed-expected.txt: Added.
* http/tests/security/contentSecurityPolicy/1.1/form-action-src-get-allowed.html: Added.
* http/tests/security/contentSecurityPolicy/1.1/form-action-src-get-blocked-expected.txt: Added.
* http/tests/security/contentSecurityPolicy/1.1/form-action-src-get-blocked.html: Added.
* http/tests/security/contentSecurityPolicy/1.1/form-action-src-javascript-blocked-expected.txt: Added.
* http/tests/security/contentSecurityPolicy/1.1/form-action-src-javascript-blocked.html: Added.
* http/tests/security/contentSecurityPolicy/1.1/form-action-src-redirect-blocked-expected.txt: Added.
* http/tests/security/contentSecurityPolicy/1.1/form-action-src-redirect-blocked.html: Added.

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

27 files changed:
LayoutTests/ChangeLog
LayoutTests/http/tests/navigation/resources/redirection-response.php
LayoutTests/http/tests/security/contentSecurityPolicy/1.1/form-action-src-allowed-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/security/contentSecurityPolicy/1.1/form-action-src-allowed.html [new file with mode: 0644]
LayoutTests/http/tests/security/contentSecurityPolicy/1.1/form-action-src-blocked-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/security/contentSecurityPolicy/1.1/form-action-src-blocked.html [new file with mode: 0644]
LayoutTests/http/tests/security/contentSecurityPolicy/1.1/form-action-src-default-ignored-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/security/contentSecurityPolicy/1.1/form-action-src-default-ignored.html [new file with mode: 0644]
LayoutTests/http/tests/security/contentSecurityPolicy/1.1/form-action-src-get-allowed-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/security/contentSecurityPolicy/1.1/form-action-src-get-allowed.html [new file with mode: 0644]
LayoutTests/http/tests/security/contentSecurityPolicy/1.1/form-action-src-get-blocked-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/security/contentSecurityPolicy/1.1/form-action-src-get-blocked.html [new file with mode: 0644]
LayoutTests/http/tests/security/contentSecurityPolicy/1.1/form-action-src-javascript-blocked-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/security/contentSecurityPolicy/1.1/form-action-src-javascript-blocked.html [new file with mode: 0644]
LayoutTests/http/tests/security/contentSecurityPolicy/1.1/form-action-src-redirect-blocked-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/security/contentSecurityPolicy/1.1/form-action-src-redirect-blocked.html [new file with mode: 0644]
LayoutTests/http/tests/security/contentSecurityPolicy/connect-src-eventsource-blocked-expected.txt
LayoutTests/http/tests/security/contentSecurityPolicy/connect-src-websocket-blocked-expected.txt
LayoutTests/http/tests/security/contentSecurityPolicy/connect-src-xmlhttprequest-blocked-expected.txt
LayoutTests/http/tests/security/contentSecurityPolicy/source-list-parsing-malformed-meta-expected.txt
LayoutTests/http/tests/security/contentSecurityPolicy/worker-connect-src-blocked-expected.txt
Source/WebCore/ChangeLog
Source/WebCore/loader/FrameLoader.cpp
Source/WebCore/loader/FrameLoader.h
Source/WebCore/loader/MainResourceLoader.cpp
Source/WebCore/page/ContentSecurityPolicy.cpp
Source/WebCore/page/ContentSecurityPolicy.h

index ef66892b494f741657026923544cf6e3d2b0693a..429257e980c442f8876185b103844d561c4abfd3 100644 (file)
@@ -1,3 +1,29 @@
+2012-08-16  Mike West  <mkwst@chromium.org>
+
+        Implement the form-action Content Security Policy directive.
+        https://bugs.webkit.org/show_bug.cgi?id=93777
+
+        Reviewed by Jochen Eisinger.
+
+        * http/tests/navigation/resources/redirection-response.php:
+            Allow for the specification of alternate hosts for the redirect. We
+            need this capability in order to check that an initial submission
+            followed by a blocked redirect results in the correct behavior.
+        * http/tests/security/contentSecurityPolicy/1.1/form-action-src-allowed-expected.txt: Added.
+        * http/tests/security/contentSecurityPolicy/1.1/form-action-src-allowed.html: Added.
+        * http/tests/security/contentSecurityPolicy/1.1/form-action-src-blocked-expected.txt: Added.
+        * http/tests/security/contentSecurityPolicy/1.1/form-action-src-blocked.html: Added.
+        * http/tests/security/contentSecurityPolicy/1.1/form-action-src-default-ignored-expected.txt: Added.
+        * http/tests/security/contentSecurityPolicy/1.1/form-action-src-default-ignored.html: Added.
+        * http/tests/security/contentSecurityPolicy/1.1/form-action-src-get-allowed-expected.txt: Added.
+        * http/tests/security/contentSecurityPolicy/1.1/form-action-src-get-allowed.html: Added.
+        * http/tests/security/contentSecurityPolicy/1.1/form-action-src-get-blocked-expected.txt: Added.
+        * http/tests/security/contentSecurityPolicy/1.1/form-action-src-get-blocked.html: Added.
+        * http/tests/security/contentSecurityPolicy/1.1/form-action-src-javascript-blocked-expected.txt: Added.
+        * http/tests/security/contentSecurityPolicy/1.1/form-action-src-javascript-blocked.html: Added.
+        * http/tests/security/contentSecurityPolicy/1.1/form-action-src-redirect-blocked-expected.txt: Added.
+        * http/tests/security/contentSecurityPolicy/1.1/form-action-src-redirect-blocked.html: Added.
+
 2012-08-16  KwangYong Choi  <ky0.choi@samsung.com>
 
         [EFL] Update pixel test expectations for layout test fast/dom/
index c9c2561d8fd099dcc63998c14a1375d430b422a2..50b23fbcf21f765640471efa0bd13c2e0e111ae2 100644 (file)
@@ -3,22 +3,26 @@ $status_code = $_GET['status'];
 
 $uri = rtrim(dirname($_SERVER['PHP_SELF']), '/\\') . "/" . $_GET['target'];
 
+$host = $_SERVER['HTTP_HOST'];
+if (isset($_GET['host']))
+    $host = $_GET['host'];
+
 switch ($status_code) {
     case 301:
         header("HTTP/1.1 301 Moved Permanently");
-        header("Location: http://" . $_SERVER['HTTP_HOST'] . $uri);
+        header("Location: http://" . $host . $uri);
         break;
     case 302:
         header("HTTP/1.1 302 Found");
-        header("Location: http://" . $_SERVER['HTTP_HOST'] . $uri);
+        header("Location: http://" . $host . $uri);
         break;
     case 303:
         header("HTTP/1.1 303 See Other");
-        header("Location: http://" . $_SERVER['HTTP_HOST'] . $uri);
+        header("Location: http://" . $host . $uri);
         break;
     case 307:
         header("HTTP/1.1 307 Temporary Redirect");
-        header("Location: http://" . $_SERVER['HTTP_HOST'] . $uri);
+        header("Location: http://" . $host . $uri);
         break;
     default:
         header("HTTP/1.1 500 Internal Server Error");
diff --git a/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/form-action-src-allowed-expected.txt b/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/form-action-src-allowed-expected.txt
new file mode 100644 (file)
index 0000000..deecd58
--- /dev/null
@@ -0,0 +1,10 @@
+This page was requested with the HTTP method POST.
+
+Parameters:
+
+fieldname = fieldvalue
+
+============== Back Forward List ==============
+        http://127.0.0.1:8000/security/contentSecurityPolicy/1.1/form-action-src-allowed.html  **nav target**
+curr->  http://127.0.0.1:8000/navigation/resources/form-target.pl  **nav target**
+===============================================
diff --git a/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/form-action-src-allowed.html b/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/form-action-src-allowed.html
new file mode 100644 (file)
index 0000000..1414988
--- /dev/null
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta http-equiv="X-WebKit-CSP" content="form-action 'self'">
+<script>
+    if (window.testRunner) {
+        testRunner.dumpAsText();
+        testRunner.waitUntilDone();
+        testRunner.clearBackForwardList();
+        testRunner.dumpBackForwardList();
+    }
+    window.addEventListener('load', function() {
+        setTimeout(function() {
+            document.getElementById('submit').click();
+        }, 0);
+    });
+</script>
+</head>
+<body>
+    <form action='/navigation/resources/form-target.pl' id='theform' method='post'>
+        <input type='text' name='fieldname' value='fieldvalue'>
+        <input type='submit' id='submit' value='submit'>
+    </form>
+
+    <p>Tests that allowed form actions work correctly. If this test passes, you will see a page indicating a form was POSTed.</p>
+</body>
+</html>
diff --git a/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/form-action-src-blocked-expected.txt b/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/form-action-src-blocked-expected.txt
new file mode 100644 (file)
index 0000000..f338fe2
--- /dev/null
@@ -0,0 +1,8 @@
+CONSOLE MESSAGE: Refused to send form data to 'http://127.0.0.1:8000/navigation/resources/form-target.pl' because it violates the following Content Security Policy directive: "form-action 'none'".
+
+  
+Tests that blocking form actions works correctly. If this test passes, you will see a console error, and will not see a page indicating a form was POSTed.
+
+============== Back Forward List ==============
+curr->  http://127.0.0.1:8000/security/contentSecurityPolicy/1.1/form-action-src-blocked.html  **nav target**
+===============================================
diff --git a/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/form-action-src-blocked.html b/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/form-action-src-blocked.html
new file mode 100644 (file)
index 0000000..216faaa
--- /dev/null
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta http-equiv="X-WebKit-CSP" content="form-action 'none'">
+<script>
+    if (window.testRunner) {
+        testRunner.dumpAsText();
+        testRunner.waitUntilDone();
+        testRunner.clearBackForwardList();
+        testRunner.dumpBackForwardList();
+    }
+    window.addEventListener('load', function() {
+        setTimeout(function() {
+            document.getElementById('submit').click();
+        }, 0);
+    });
+    setTimeout(function () {
+        testRunner.notifyDone();
+    }, 1000);
+</script>
+</head>
+<body>
+    <form action='/navigation/resources/form-target.pl' id='theform' method='post'>
+        <input type='text' name='fieldname' value='fieldvalue'>
+        <input type='submit' id='submit' value='submit'>
+    </form>
+
+    <p>Tests that blocking form actions works correctly. If this test passes, you will see a console error, and will not see a page indicating a form was POSTed.</p>
+</body>
+</html>
diff --git a/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/form-action-src-default-ignored-expected.txt b/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/form-action-src-default-ignored-expected.txt
new file mode 100644 (file)
index 0000000..d0c9cda
--- /dev/null
@@ -0,0 +1,10 @@
+This page was requested with the HTTP method POST.
+
+Parameters:
+
+fieldname = fieldvalue
+
+============== Back Forward List ==============
+        http://127.0.0.1:8000/security/contentSecurityPolicy/1.1/form-action-src-default-ignored.html  **nav target**
+curr->  http://127.0.0.1:8000/navigation/resources/form-target.pl  **nav target**
+===============================================
diff --git a/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/form-action-src-default-ignored.html b/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/form-action-src-default-ignored.html
new file mode 100644 (file)
index 0000000..97a3b28
--- /dev/null
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta http-equiv="X-WebKit-CSP" content="default-src 'none'; script-src 'unsafe-inline'">
+<script>
+    if (window.testRunner) {
+        testRunner.dumpAsText();
+        testRunner.waitUntilDone();
+        testRunner.clearBackForwardList();
+        testRunner.dumpBackForwardList();
+    }
+    window.addEventListener('load', function() {
+        setTimeout(function() {
+            document.getElementById('submit').click();
+        }, 0);
+    });
+</script>
+</head>
+<body>
+    <form action='/navigation/resources/form-target.pl' id='theform' method='post'>
+        <input type='text' name='fieldname' value='fieldvalue'>
+        <input type='submit' id='submit' value='submit'>
+    </form>
+
+    <p>Tests that default-src does. If this test passes, you will see a page indicating a form was POSTed.</p>
+</body>
+</html>
diff --git a/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/form-action-src-get-allowed-expected.txt b/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/form-action-src-get-allowed-expected.txt
new file mode 100644 (file)
index 0000000..548b415
--- /dev/null
@@ -0,0 +1,10 @@
+This page was requested with the HTTP method GET.
+
+Parameters:
+
+fieldname = fieldvalue
+
+============== Back Forward List ==============
+        http://127.0.0.1:8000/security/contentSecurityPolicy/1.1/form-action-src-get-allowed.html  **nav target**
+curr->  http://127.0.0.1:8000/navigation/resources/form-target.pl?fieldname=fieldvalue  **nav target**
+===============================================
diff --git a/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/form-action-src-get-allowed.html b/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/form-action-src-get-allowed.html
new file mode 100644 (file)
index 0000000..b39a567
--- /dev/null
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta http-equiv="X-WebKit-CSP" content="form-action 'self'">
+<script>
+    if (window.testRunner) {
+        testRunner.dumpAsText();
+        testRunner.waitUntilDone();
+        testRunner.clearBackForwardList();
+        testRunner.dumpBackForwardList();
+    }
+    window.addEventListener('load', function() {
+        setTimeout(function() {
+            document.getElementById('submit').click();
+        }, 0);
+    });
+</script>
+</head>
+<body>
+    <form action='/navigation/resources/form-target.pl' id='theform' method='get'>
+        <input type='text' name='fieldname' value='fieldvalue'>
+        <input type='submit' id='submit' value='submit'>
+    </form>
+
+    <p>Tests that allowed form actions work correctly. If this test passes, you will see a page indicating a form was POSTed.</p>
+</body>
+</html>
diff --git a/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/form-action-src-get-blocked-expected.txt b/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/form-action-src-get-blocked-expected.txt
new file mode 100644 (file)
index 0000000..dc64ce0
--- /dev/null
@@ -0,0 +1,8 @@
+CONSOLE MESSAGE: Refused to send form data to 'http://127.0.0.1:8000/navigation/resources/form-target.pl?fieldname=fieldvalue' because it violates the following Content Security Policy directive: "form-action 'none'".
+
+  
+Tests that blocking form actions works correctly. If this test passes, you will see a console error, and will not see a page indicating a form was POSTed.
+
+============== Back Forward List ==============
+curr->  http://127.0.0.1:8000/security/contentSecurityPolicy/1.1/form-action-src-get-blocked.html  **nav target**
+===============================================
diff --git a/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/form-action-src-get-blocked.html b/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/form-action-src-get-blocked.html
new file mode 100644 (file)
index 0000000..1077b6f
--- /dev/null
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta http-equiv="X-WebKit-CSP" content="form-action 'none'">
+<script>
+    if (window.testRunner) {
+        testRunner.dumpAsText();
+        testRunner.waitUntilDone();
+        testRunner.clearBackForwardList();
+        testRunner.dumpBackForwardList();
+    }
+    window.addEventListener('load', function() {
+        setTimeout(function () {
+            testRunner.notifyDone();
+        }, 1000);
+        document.getElementById('submit').click();
+    });
+</script>
+</head>
+<body>
+    <form action='/navigation/resources/form-target.pl' id='theform' method='get'>
+        <input type='text' name='fieldname' value='fieldvalue'>
+        <input type='submit' id='submit' value='submit'>
+    </form>
+
+    <p>Tests that blocking form actions works correctly. If this test passes, you will see a console error, and will not see a page indicating a form was POSTed.</p>
+</body>
+</html>
diff --git a/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/form-action-src-javascript-blocked-expected.txt b/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/form-action-src-javascript-blocked-expected.txt
new file mode 100644 (file)
index 0000000..9ed6e94
--- /dev/null
@@ -0,0 +1,8 @@
+CONSOLE MESSAGE: Refused to send form data to 'javascript:alert("FAIL!")' because it violates the following Content Security Policy directive: "form-action 'none'".
+
+  
+Tests that blocking form actions works correctly. If this test passes, you will see a console error, and will not see a JavaScript alert.
+
+============== Back Forward List ==============
+curr->  http://127.0.0.1:8000/security/contentSecurityPolicy/1.1/form-action-src-javascript-blocked.html  **nav target**
+===============================================
diff --git a/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/form-action-src-javascript-blocked.html b/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/form-action-src-javascript-blocked.html
new file mode 100644 (file)
index 0000000..62d6b95
--- /dev/null
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta http-equiv="X-WebKit-CSP" content="form-action 'none'">
+<script>
+    if (window.testRunner) {
+        testRunner.dumpAsText();
+        testRunner.waitUntilDone();
+        testRunner.clearBackForwardList();
+        testRunner.dumpBackForwardList();
+    }
+    window.addEventListener('load', function() {
+        setTimeout(function() {
+            document.getElementById('submit').click();
+        }, 0);
+    });
+    setTimeout(function () {
+        testRunner.notifyDone();
+    }, 1000);
+</script>
+</head>
+<body>
+    <form action='javascript:alert("FAIL!")' id='theform' method='post'>
+        <input type='text' name='fieldname' value='fieldvalue'>
+        <input type='submit' id='submit' value='submit'>
+    </form>
+
+    <p>Tests that blocking form actions works correctly. If this test passes, you will see a console error, and will not see a JavaScript alert.</p>
+</body>
+</html>
diff --git a/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/form-action-src-redirect-blocked-expected.txt b/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/form-action-src-redirect-blocked-expected.txt
new file mode 100644 (file)
index 0000000..f1fdf63
--- /dev/null
@@ -0,0 +1,8 @@
+CONSOLE MESSAGE: Refused to send form data to 'http://localhost:8000/navigation/resources/form-target.pl' because it violates the following Content Security Policy directive: "form-action 127.0.0.1:8000".
+
+  
+Tests that blocking form redirect works correctly. If this test passes, you will see a console error, and will not see a page indicating a form was POSTed.
+
+============== Back Forward List ==============
+curr->  http://127.0.0.1:8000/security/contentSecurityPolicy/1.1/form-action-src-redirect-blocked.html  **nav target**
+===============================================
diff --git a/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/form-action-src-redirect-blocked.html b/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/form-action-src-redirect-blocked.html
new file mode 100644 (file)
index 0000000..eb8c0eb
--- /dev/null
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta http-equiv="X-WebKit-CSP" content="form-action 127.0.0.1:8000">
+<script>
+    if (window.testRunner) {
+        testRunner.dumpAsText();
+        testRunner.waitUntilDone();
+        testRunner.clearBackForwardList();
+        testRunner.dumpBackForwardList();
+    }
+    window.addEventListener('load', function() {
+        setTimeout(function() {
+            document.getElementById('submit').click();
+        }, 0);
+    });
+    setTimeout(function () {
+        testRunner.notifyDone();
+    }, 1000);
+</script>
+</head>
+<body>
+    <form id="form1" action="/navigation/resources/redirection-response.php?host=localhost:8000&status=302&target=form-target.pl" method="post">
+        <input type='text' name='fieldname' value='fieldvalue'>
+        <input type='submit' id='submit' value='submit'>
+    </form>
+
+    <p>Tests that blocking form redirect works correctly. If this test passes, you will see a console error, and will not see a page indicating a form was POSTed.</p>
+</body>
+</html>
index 5f8437f3a9c50554ea71f35e2be367458bf02f72..acfda621609a58e6f9eb4f7858188b07e1577c1e 100644 (file)
@@ -1,4 +1,4 @@
-CONSOLE MESSAGE: Refused to connect to connect 'http://127.0.0.1:8000/eventsource/resources/simple-event-stream.asis' because it violates the following Content Security Policy directive: "connect-src http://localhost:8000".
+CONSOLE MESSAGE: Refused to connect to 'http://127.0.0.1:8000/eventsource/resources/simple-event-stream.asis' because it violates the following Content Security Policy directive: "connect-src http://localhost:8000".
 
 Pass
 
index ec0958e4c24c1d9a20eef6d87ef3515a0b03aaa2..2d958f78bd06c9d16aa0d579e2f0b89d125f1023 100644 (file)
@@ -1,4 +1,4 @@
-CONSOLE MESSAGE: Refused to connect to connect 'ws://localhost:8880/websocket/tests/hybi/echo' because it violates the following Content Security Policy directive: "connect-src ws://127.0.0.1:8880".
+CONSOLE MESSAGE: Refused to connect to 'ws://localhost:8880/websocket/tests/hybi/echo' because it violates the following Content Security Policy directive: "connect-src ws://127.0.0.1:8880".
 
 Pass
 
index 1c22fc609c4b83c5e61111991b3a3a7472410804..14d284c1f867c6c538106db5ba9fdad7e759ea8c 100644 (file)
@@ -1,4 +1,4 @@
-CONSOLE MESSAGE: Refused to connect to connect 'http://localhost:8000/xmlhttprequest/resources/get.txt' because it violates the following Content Security Policy directive: "connect-src http://127.0.0.1:8000".
+CONSOLE MESSAGE: Refused to connect to 'http://localhost:8000/xmlhttprequest/resources/get.txt' because it violates the following Content Security Policy directive: "connect-src http://127.0.0.1:8000".
 
 Pass
 
index b3c5e3c34d37b0312d53aec2be38263c81461265..c3dd20ba5a50883d078cd998bf7b986b98f7182c 100644 (file)
@@ -1,4 +1,4 @@
-CONSOLE MESSAGE: Refused to connect to connect 'http://127.0.0.1:8000/xmlhttprequest/resources/get.txt' because it violates the following Content Security Policy directive: "connect-src http://localhost:8000".
+CONSOLE MESSAGE: Refused to connect to 'http://127.0.0.1:8000/xmlhttprequest/resources/get.txt' because it violates the following Content Security Policy directive: "connect-src http://localhost:8000".
 
 Pass
 This test passes if the malformed meta tag doesn't cause a crash and the resource is blocked.
index 24e8babae3694d8688f2e865410f7a8a041061ca..8a7a7746c45462952ac757ea75431e110cead1cc 100644 (file)
@@ -1,4 +1,4 @@
-CONSOLE MESSAGE: Refused to connect to connect 'http://127.0.0.1:8000/xmlhttprequest/resources/get.txt' because it violates the following Content Security Policy directive: "connect-src 'none'".
+CONSOLE MESSAGE: Refused to connect to 'http://127.0.0.1:8000/xmlhttprequest/resources/get.txt' because it violates the following Content Security Policy directive: "connect-src 'none'".
 
 ALERT: xhr blocked
 
index ae8e2ffbaa9f5d00d8428dc68c4fc5c47d3ba48c..7559835d40f4d842169cb9079a4c53aa416b1203 100644 (file)
@@ -1,3 +1,59 @@
+2012-08-16  Mike West  <mkwst@chromium.org>
+
+        Implement the form-action Content Security Policy directive.
+        https://bugs.webkit.org/show_bug.cgi?id=93777
+
+        Reviewed by Jochen Eisinger.
+
+        The CSP 1.1 editor's draft defines the 'form-action' directive as a
+        mechanism for whitelisting valid targets for form submission from a
+        protected resource. A web author might desire to restrict form
+        submissions to the same origin as the protected resource itself via
+        a Content Security Policy of "form-action 'self'", or ensure that all
+        submissions were sent over an SSL connection via "form-action https:".
+
+        Specification details available at: https://dvcs.w3.org/hg/content-security-policy/raw-file/tip/csp-specification.dev.html#form-action--experimental
+
+        This experimental directive is gated on the ENABLE_CSP_NEXT flag, which
+        is currently only enabled in Chromium.
+
+        Tests: http/tests/security/contentSecurityPolicy/1.1/form-action-src-allowed.html
+               http/tests/security/contentSecurityPolicy/1.1/form-action-src-blocked.html
+               http/tests/security/contentSecurityPolicy/1.1/form-action-src-default-ignored.html
+               http/tests/security/contentSecurityPolicy/1.1/form-action-src-get-allowed.html
+               http/tests/security/contentSecurityPolicy/1.1/form-action-src-get-blocked.html
+               http/tests/security/contentSecurityPolicy/1.1/form-action-src-javascript-blocked.html
+               http/tests/security/contentSecurityPolicy/1.1/form-action-src-redirect-blocked.html
+
+        * loader/FrameLoader.cpp:
+        (WebCore::FrameLoader::checkIfFormActionAllowedByCSP):
+            Adding a callback to FrameLoader in order to allow the
+            MainResourceLoader to check the relevant CSP status without knowing
+            anything about CSP.
+        (WebCore):
+        * loader/FrameLoader.h:
+        (FrameLoader):
+        * loader/MainResourceLoader.cpp:
+        (WebCore::MainResourceLoader::willSendRequest):
+            Check against the protected resource's Content Security Policy when
+            presented with a request that is itself a form submission, or is the
+            result of a redirect in response to a form submission. If CSP would
+            block the target, cancel the request.
+        * page/ContentSecurityPolicy.cpp:
+        (CSPDirectiveList):
+        (WebCore::CSPDirectiveList::checkSourceAndReportViolation):
+            Added explanatory text to the source violation console warning that
+            specifically calls out sending form data (as opposed to "connect to"
+            or "load the").
+        (WebCore::CSPDirectiveList::allowFormAction):
+            Check a URL against a directive list's the 'form-action' source list.
+        (WebCore):
+        (WebCore::CSPDirectiveList::addDirective):
+            Recognize the 'form-action' CSP directive.
+        (WebCore::ContentSecurityPolicy::allowFormAction):
+            Public interface to check a form action.
+        * page/ContentSecurityPolicy.h:
+
 2012-08-16  Arvid Nilsson  <anilsson@rim.com>
 
         [BlackBerry] WebGL and Canvas fail to display after being restored from page cache
index 97fa49f08213ccd697ed73f4885cdc2241fb1dd6..1db94893fc9c4f0c184e6476e5cc75ac837ac6ae 100644 (file)
@@ -303,6 +303,8 @@ void FrameLoader::submitForm(PassRefPtr<FormSubmission> submission)
         return;
 
     if (protocolIsJavaScript(submission->action())) {
+        if (!m_frame->document()->contentSecurityPolicy()->allowFormAction(KURL(submission->action())))
+            return;
         m_isExecutingJavaScriptFormAction = true;
         m_frame->script()->executeIfJavaScriptURL(submission->action(), DoNotReplaceDocumentIfJavaScriptURL);
         m_isExecutingJavaScriptFormAction = false;
@@ -912,6 +914,14 @@ bool FrameLoader::checkIfRunInsecureContent(SecurityOrigin* context, const KURL&
     return allowed;
 }
 
+bool FrameLoader::checkIfFormActionAllowedByCSP(const KURL& url) const
+{
+    if (m_submittedFormURL.isEmpty())
+        return true;
+
+    return m_frame->document()->contentSecurityPolicy()->allowFormAction(url);
+}
+
 Frame* FrameLoader::opener()
 {
     return m_opener;
index b12dd48778d8bd64a4951d4e13412bcbbcc35d22..4a17ab248d5f1b12dafda3fd6c13abdf4547fd42 100644 (file)
@@ -222,6 +222,8 @@ public:
     bool checkIfDisplayInsecureContent(SecurityOrigin* context, const KURL&);
     bool checkIfRunInsecureContent(SecurityOrigin* context, const KURL&);
 
+    bool checkIfFormActionAllowedByCSP(const KURL&) const;
+
     Frame* opener();
     void setOpener(Frame*);
 
index 0b065dd833c08dff995d4b70804ea92ca4117788..0836d8cc00e89058e98323bb6d606453fce9c537 100644 (file)
@@ -204,6 +204,11 @@ void MainResourceLoader::willSendRequest(ResourceRequest& newRequest, const Reso
     // reference to this object; one example of this is 3266216.
     RefPtr<MainResourceLoader> protect(this);
 
+    if (!frameLoader()->checkIfFormActionAllowedByCSP(newRequest.url())) {
+        cancel();
+        return;
+    }
+
     ASSERT(documentLoader()->timing()->fetchStart());
     if (!redirectResponse.isNull()) {
         // If the redirecting url is not allowed to display content from the target origin,
index e809d7cda04c051d0eb3e8f0965f3883b89ba4e9..f88db30b9b3991c93907a56ff55530dd763bbfa7 100644 (file)
@@ -582,6 +582,7 @@ public:
     bool allowFontFromSource(const KURL&, ContentSecurityPolicy::ReportingStatus) const;
     bool allowMediaFromSource(const KURL&, ContentSecurityPolicy::ReportingStatus) const;
     bool allowConnectToSource(const KURL&, ContentSecurityPolicy::ReportingStatus) const;
+    bool allowFormAction(const KURL&, ContentSecurityPolicy::ReportingStatus) const;
 
     void gatherReportURIs(DOMStringList&) const;
 
@@ -631,6 +632,7 @@ private:
     OwnPtr<CSPDirective> m_fontSrc;
     OwnPtr<CSPDirective> m_mediaSrc;
     OwnPtr<CSPDirective> m_connectSrc;
+    OwnPtr<CSPDirective> m_formAction;
 
     Vector<KURL> m_reportURIs;
     HashSet<String> m_pluginTypes;
@@ -742,8 +744,14 @@ bool CSPDirectiveList::checkSourceAndReportViolation(CSPDirective* directive, co
 {
     if (checkSource(directive, url))
         return true;
-    String verb = type == "connect" ? "connect to" : "load the";
-    reportViolation(directive->text(), "Refused to " + verb + " " + type + " '" + url.string() + "' because it violates the following Content Security Policy directive: \"" + directive->text() + "\".\n", url);
+
+    String prefix = makeString("Refused to load the ", type, " '");
+    if (type == "connect")
+        prefix = "Refused to connect to '";
+    if (type == "form")
+        prefix = "Refused to send form data to '";
+
+    reportViolation(directive->text(), makeString(prefix, url.string(), "' because it violates the following Content Security Policy directive: \"", directive->text(), "\".\n"), url);
     return denyIfEnforcingPolicy();
 }
 
@@ -884,6 +892,14 @@ void CSPDirectiveList::gatherReportURIs(DOMStringList& list) const
         list.append(m_reportURIs[i].string());
 }
 
+bool CSPDirectiveList::allowFormAction(const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
+{
+    DEFINE_STATIC_LOCAL(String, type, ("form"));
+    return reportingStatus == ContentSecurityPolicy::SendReport ?
+        checkSourceAndReportViolation(m_formAction.get(), url, type) :
+        checkSource(m_formAction.get(), url);
+}
+
 // policy            = directive-list
 // directive-list    = [ directive *( ";" [ directive ] ) ]
 //
@@ -1116,8 +1132,9 @@ void CSPDirectiveList::addDirective(const String& name, const String& value)
     DEFINE_STATIC_LOCAL(String, sandbox, ("sandbox"));
     DEFINE_STATIC_LOCAL(String, reportURI, ("report-uri"));
 #if ENABLE(CSP_NEXT)
-    DEFINE_STATIC_LOCAL(String, scriptNonce, ("script-nonce"));
+    DEFINE_STATIC_LOCAL(String, formAction, ("form-action"));
     DEFINE_STATIC_LOCAL(String, pluginTypes, ("plugin-types"));
+    DEFINE_STATIC_LOCAL(String, scriptNonce, ("script-nonce"));
 #endif
 
     ASSERT(!name.isEmpty());
@@ -1145,10 +1162,12 @@ void CSPDirectiveList::addDirective(const String& name, const String& value)
     else if (equalIgnoringCase(name, reportURI))
         parseReportURI(name, value);
 #if ENABLE(CSP_NEXT)
-    else if (equalIgnoringCase(name, scriptNonce))
-        parseScriptNonce(name, value);
+    else if (equalIgnoringCase(name, formAction))
+        setCSPDirective(name, value, m_formAction);
     else if (equalIgnoringCase(name, pluginTypes))
         parsePluginTypes(name, value);
+    else if (equalIgnoringCase(name, scriptNonce))
+        parseScriptNonce(name, value);
 #endif
     else
         m_policy->reportUnrecognizedDirective(name);
@@ -1335,6 +1354,11 @@ bool ContentSecurityPolicy::allowConnectToSource(const KURL& url, ContentSecurit
     return isAllowedByAllWithURL<&CSPDirectiveList::allowConnectToSource>(m_policies, url, reportingStatus);
 }
 
+bool ContentSecurityPolicy::allowFormAction(const KURL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
+{
+    return isAllowedByAllWithURL<&CSPDirectiveList::allowFormAction>(m_policies, url, reportingStatus);
+}
+
 bool ContentSecurityPolicy::isActive() const
 {
     return !m_policies.isEmpty();
index 72a106eae3871868ff9f6ca87d11149ef8ca4948..3c390516aace5bde3378a88e8bf6d300b3997d23 100644 (file)
@@ -91,6 +91,7 @@ public:
     bool allowFontFromSource(const KURL&, ReportingStatus = SendReport) const;
     bool allowMediaFromSource(const KURL&, ReportingStatus = SendReport) const;
     bool allowConnectToSource(const KURL&, ReportingStatus = SendReport) const;
+    bool allowFormAction(const KURL&, ReportingStatus = SendReport) const;
 
     void setOverrideAllowInlineStyle(bool);