You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by ga...@apache.org on 2014/06/19 08:02:22 UTC

[05/50] [abbrv] couchdb commit: updated refs/heads/Update-Sidebar-Ui to d34dfae

Add Experimental Content-Security-Policy-Support (CSP) for Fauxton

Like every web application, Fauxton is vulnerable against XSS and
CSP is a technology that tries to help against that.

The patch makes it possible to enable CSP for the /_utils path and
allows configuration of the sent header.

The default setting for the value of the header breaks the old
Futon, when CSP is enabled there. The old Futon has alot of
inline-JavaScript which is not allowed in the setting I have
chosen as default.

For development, the header is also sent from the Node server
which launches Fauxton in dev-mode.

People can enable the feature by setting enable = true in the
section [csp] of their configs


Project: http://git-wip-us.apache.org/repos/asf/couchdb/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb/commit/3bcf664b
Tree: http://git-wip-us.apache.org/repos/asf/couchdb/tree/3bcf664b
Diff: http://git-wip-us.apache.org/repos/asf/couchdb/diff/3bcf664b

Branch: refs/heads/Update-Sidebar-Ui
Commit: 3bcf664b2f46750bf64bf970da07f9b133f98047
Parents: 698f55b
Author: Robert Kowalski <ro...@kowalski.gd>
Authored: Sat May 17 18:37:30 2014 +0200
Committer: Robert Kowalski <ro...@kowalski.gd>
Committed: Mon Jun 9 19:09:06 2014 +0200

----------------------------------------------------------------------
 etc/couchdb/default.ini.tpl.in            |  4 ++
 share/doc/src/config/misc.rst             | 24 ++++++++
 share/doc/src/experimental.rst            | 15 +++++
 src/couchdb/couch_httpd_misc_handlers.erl | 16 ++++-
 src/fauxton/tasks/couchserver.js          |  4 ++
 test/etap/232-csp.t                       | 85 ++++++++++++++++++++++++++
 6 files changed, 145 insertions(+), 3 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb/blob/3bcf664b/etc/couchdb/default.ini.tpl.in
----------------------------------------------------------------------
diff --git a/etc/couchdb/default.ini.tpl.in b/etc/couchdb/default.ini.tpl.in
index 934c6cd..4c29faa 100644
--- a/etc/couchdb/default.ini.tpl.in
+++ b/etc/couchdb/default.ini.tpl.in
@@ -88,6 +88,10 @@ credentials = false
 ; List of accepted methods
 ; methods =
 
+; Experimental CSP (Content Security Policy) Support for _utils
+[csp]
+enable = false
+; header_value = default-src 'self'; img-src 'self'; font-src *; script-src 'self' 'unsafe-eval'; style-src 'self' 'unsafe-inline';
 
 ; Configuration for a vhost
 ;[cors:http://example.com]

http://git-wip-us.apache.org/repos/asf/couchdb/blob/3bcf664b/share/doc/src/config/misc.rst
----------------------------------------------------------------------
diff --git a/share/doc/src/config/misc.rst b/share/doc/src/config/misc.rst
index 58d079c..e97575a 100644
--- a/share/doc/src/config/misc.rst
+++ b/share/doc/src/config/misc.rst
@@ -232,3 +232,27 @@ Vendor information
     [vendor]
     name = The Apache Software Foundation
     version = 1.5.0
+
+.. _config/csp:
+
+Content-Security-Policy
+=======================
+
+.. config:section:: csp :: Content-Security-Policy
+
+  Experimental support of CSP Headers for ``/_utils`` (Fauxton).
+
+  .. config:option:: enable
+
+    Enable the sending of the Header ``Content-Security-Policy``::
+
+      [csp]
+      enable = true
+
+
+  .. config:option:: header_value
+
+    You can change the default value for the Header which is sent::
+
+      [csp]
+      header_value = default-src 'self'; img-src *; font-src *;

http://git-wip-us.apache.org/repos/asf/couchdb/blob/3bcf664b/share/doc/src/experimental.rst
----------------------------------------------------------------------
diff --git a/share/doc/src/experimental.rst b/share/doc/src/experimental.rst
index 3157f53..fae925c 100644
--- a/share/doc/src/experimental.rst
+++ b/share/doc/src/experimental.rst
@@ -81,3 +81,18 @@ Plugins
 See `src/couch_plugins/README.md`.
 
 
+Content-Security-Policy (CSP) Header Support for /_utils (Fauxton)
+==================================================================
+
+This will just work with Fauxton, and not Futon. You can enable it
+in your config: you can enable the feature in general and change
+the default header that is sent for everything in /_utils.
+
+    .. code-block:: ini
+
+      [csp]
+      enable = true
+
+Then restart CouchDB.
+
+Have fun!

http://git-wip-us.apache.org/repos/asf/couchdb/blob/3bcf664b/src/couchdb/couch_httpd_misc_handlers.erl
----------------------------------------------------------------------
diff --git a/src/couchdb/couch_httpd_misc_handlers.erl b/src/couchdb/couch_httpd_misc_handlers.erl
index c86f5c7..51e8156 100644
--- a/src/couchdb/couch_httpd_misc_handlers.erl
+++ b/src/couchdb/couch_httpd_misc_handlers.erl
@@ -68,9 +68,10 @@ handle_utils_dir_req(#httpd{method='GET'}=Req, DocumentRoot) ->
     case couch_httpd:partition(UrlPath) of
     {_ActionKey, "/", RelativePath} ->
         % GET /_utils/path or GET /_utils/
-        CachingHeaders =
-                [{"Cache-Control", "private, must-revalidate"}],
-        couch_httpd:serve_file(Req, RelativePath, DocumentRoot, CachingHeaders);
+        CachingHeaders = [{"Cache-Control", "private, must-revalidate"}],
+        EnableCsp = couch_config:get("csp", "enable", "false"),
+        Headers = maybe_add_csp_headers(CachingHeaders, EnableCsp),
+        couch_httpd:serve_file(Req, RelativePath, DocumentRoot, Headers);
     {_ActionKey, "", _RelativePath} ->
         % GET /_utils
         RedirectPath = couch_httpd:path(Req) ++ "/",
@@ -79,6 +80,15 @@ handle_utils_dir_req(#httpd{method='GET'}=Req, DocumentRoot) ->
 handle_utils_dir_req(Req, _) ->
     send_method_not_allowed(Req, "GET,HEAD").
 
+maybe_add_csp_headers(Headers, "true") ->
+    DefaultValues = "default-src 'self'; img-src 'self'; font-src 'self'; "
+                    "script-src 'self' 'unsafe-eval'; style-src 'self' 'unsafe-inline';",
+    Value = couch_config:get("csp", "header_value", DefaultValues),
+    [{"Content-Security-Policy", Value} | Headers];
+maybe_add_csp_headers(Headers, _) ->
+    Headers.
+
+
 handle_all_dbs_req(#httpd{method='GET'}=Req) ->
     {ok, DbNames} = couch_server:all_databases(),
     send_json(Req, DbNames);

http://git-wip-us.apache.org/repos/asf/couchdb/blob/3bcf664b/src/fauxton/tasks/couchserver.js
----------------------------------------------------------------------
diff --git a/src/fauxton/tasks/couchserver.js b/src/fauxton/tasks/couchserver.js
index 67b0ae0..3a17ab6 100644
--- a/src/fauxton/tasks/couchserver.js
+++ b/src/fauxton/tasks/couchserver.js
@@ -47,6 +47,10 @@ module.exports = function (grunt) {
           accept = req.headers.accept.split(','),
           filePath;
 
+      var headerValue = "default-src 'self'; img-src 'self'; font-src 'self'; " +
+                        "script-src 'self' 'unsafe-eval'; style-src 'self' 'unsafe-inline';";
+      res.setHeader('Content-Security-Policy', headerValue);
+
       if (!!url.match(/^\/addons\/.*\/assets\/js/)) {
         filePath = path.join(app_dir, url.replace('/_utils/fauxton/',''));
       } else if (!!url.match(/assets/)) {

http://git-wip-us.apache.org/repos/asf/couchdb/blob/3bcf664b/test/etap/232-csp.t
----------------------------------------------------------------------
diff --git a/test/etap/232-csp.t b/test/etap/232-csp.t
new file mode 100644
index 0000000..6dbce6a
--- /dev/null
+++ b/test/etap/232-csp.t
@@ -0,0 +1,85 @@
+#!/usr/bin/env escript
+%% -*- erlang -*-
+
+% Licensed under the Apache License, Version 2.0 (the "License"); you may not
+% use this file except in compliance with the License. You may obtain a copy of
+% the License at
+%
+%   http://www.apache.org/licenses/LICENSE-2.0
+%
+% Unless required by applicable law or agreed to in writing, software
+% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+% License for the specific language governing permissions and limitations under
+% the License.
+
+server() ->
+    lists:concat([
+        "http://127.0.0.1:",
+        mochiweb_socket_server:get(couch_httpd, port),
+        "/_utils/"
+    ]).
+
+
+main(_) ->
+    test_util:init_code_path(),
+
+    etap:plan(3),
+    case (catch test()) of
+        ok ->
+            etap:end_tests();
+        Other ->
+            etap:diag(io_lib:format("Test died abnormally: ~p", [Other])),
+            etap:bail(Other)
+    end,
+    ok.
+
+test() ->
+    %% launch couchdb
+    couch_server_sup:start_link(test_util:config_files()),
+
+    % CSP is disabled by default
+    test_no_csp_headers_server(),
+
+    % Now enable CSP
+    ok = couch_config:set("csp", "enable", "true", false),
+
+    test_default_header_value(),
+
+    ok = couch_config:set("csp", "header_value", "default-src 'http://example.com';", false),
+    test_custom_header_value(),
+
+    % Disabled on all other values than true
+    ok = couch_config:set("csp", "enable", "blerg", false),
+    test_all_other_values_for_enable(),
+
+    timer:sleep(3000),
+    couch_server_sup:stop(),
+    ok.
+
+test_no_csp_headers_server() ->
+    Headers = [{"Origin", "http://127.0.0.1"}],
+    {ok, _, Resp, _} = ibrowse:send_req(server(), Headers, get, []),
+    etap:is(proplists:get_value("Content-Security-Policy", Resp),
+            undefined, "No CSP Headers when disabled").
+
+test_default_header_value() ->
+    Headers = [{"Origin", "http://127.0.0.1"}],
+    {ok, _, Resp, _} = ibrowse:send_req(server(), Headers, get, []),
+    etap:is(proplists:get_value("Content-Security-Policy", Resp),
+            "default-src 'self'; img-src 'self'; font-src 'self'; "
+            "script-src 'self' 'unsafe-eval'; style-src 'self' 'unsafe-inline';",
+            "Default CSP Headers when enabled").
+
+test_custom_header_value() ->
+    Headers = [{"Origin", "http://127.0.0.1"}],
+    {ok, _, Resp, _} = ibrowse:send_req(server(), Headers, get, []),
+    etap:is(proplists:get_value("Content-Security-Policy", Resp),
+            "default-src 'http://example.com';",
+            "Custom CSP Headers possible").
+
+test_all_other_values_for_enable() ->
+    Headers = [{"Origin", "http://127.0.0.1"}],
+    {ok, _, Resp, _} = ibrowse:send_req(server(), Headers, get, []),
+    etap:is(proplists:get_value("Content-Security-Policy", Resp),
+            undefined, "No CSP Headers when wrong value given").