This is an automated email from the ASF dual-hosted git repository.

alexey pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/kudu.git


The following commit(s) were added to refs/heads/master by this push:
     new 43ee785b2 [www] Add CSP header to web UI
43ee785b2 is described below

commit 43ee785b2d793fe9abaae91ad152c50813e9aa59
Author: Attila Bukor <[email protected]>
AuthorDate: Wed Mar 2 18:31:22 2022 +0100

    [www] Add CSP header to web UI
    
    CSP (Content Security Policy) headers provide a way to tell the browser
    where assets can be loaded from to prevent XSS attacks. Kudu's web UI is
    read-only, at least for now, so it's not susceptible for XSS attacks,
    but some security scanners still flag it as vulnerable due to not having
    this header.
    
    This patch adds a CSP header that allows loading assets on the same
    host, and some inline styles and images in jQuery. It also removes all
    inline style definitions from first-party files and moves them to
    kudu.css.
    
    There's no good way to write a unit test for this, as it requires a
    GUI browser (curl doesn't load external resources and doesn't use
    JavaScript), but I tested it manually both through HTTP and HTTPS and
    confirmed there are no related errors in the JS console.
    
    Change-Id: I411d8f4ca079bfd5584f563aeeaa867833eb1106
    Reviewed-on: http://gerrit.cloudera.org:8080/18285
    Tested-by: Kudu Jenkins
    Reviewed-by: Alexey Serbin <[email protected]>
---
 src/kudu/server/webserver-test.cc | 20 ++++++++++++++++++++
 src/kudu/server/webserver.cc      | 15 ++++++++++++++-
 www/kudu.css                      | 12 ++++++++++++
 www/startup.mustache              |  8 ++++----
 4 files changed, 50 insertions(+), 5 deletions(-)

diff --git a/src/kudu/server/webserver-test.cc 
b/src/kudu/server/webserver-test.cc
index 6fa2a02d3..b62120c29 100644
--- a/src/kudu/server/webserver-test.cc
+++ b/src/kudu/server/webserver-test.cc
@@ -63,6 +63,8 @@ DECLARE_int32(webserver_max_post_length_bytes);
 DEFINE_bool(test_sensitive_flag, false, "a sensitive flag");
 TAG_FLAG(test_sensitive_flag, sensitive);
 
+DECLARE_bool(webserver_enable_csp);
+
 namespace kudu {
 
 namespace {
@@ -453,6 +455,24 @@ TEST_F(WebserverTest, TestRedactFlagsDump) {
                                                   kRedactionMessage));
 }
 
+TEST_F(WebserverTest, TestCSPHeader) {
+  constexpr const char* kCspHeader = "Content-Security-Policy";
+
+  curl_.set_return_headers(true);
+  ASSERT_OK(curl_.FetchURL(url_, &buf_));
+  // Basic sanity check: the page should have the expected title.
+  ASSERT_STR_CONTAINS(buf_.ToString(), "Kudu");
+
+  // The CSP policy is enabled by default for the embedded Kudu webserver.
+  ASSERT_OK(curl_.FetchURL(url_, &buf_));
+  ASSERT_STR_CONTAINS(buf_.ToString(), kCspHeader);
+
+  // Check if response doesn't contain CSP header when disabled.
+  FLAGS_webserver_enable_csp = false;
+  ASSERT_OK(curl_.FetchURL(url_, &buf_));
+  ASSERT_STR_NOT_CONTAINS(buf_.ToString(), kCspHeader);
+}
+
 // Used in symbolization test below.
 void SomeMethodForSymbolTest1() {}
 // Used in symbolization test below.
diff --git a/src/kudu/server/webserver.cc b/src/kudu/server/webserver.cc
index a9253de84..79732357f 100644
--- a/src/kudu/server/webserver.cc
+++ b/src/kudu/server/webserver.cc
@@ -90,6 +90,11 @@ DEFINE_string(webserver_x_frame_options, "DENY",
               "to all responses. This can help prevent clickjacking attacks.");
 TAG_FLAG(webserver_x_frame_options, advanced);
 
+DEFINE_bool(webserver_enable_csp, true,
+            "The webserver adds the Content-Security-Policy header to response 
when enabled.");
+TAG_FLAG(webserver_enable_csp, advanced);
+TAG_FLAG(webserver_enable_csp, runtime);
+
 
 namespace kudu {
 
@@ -681,10 +686,18 @@ void Webserver::SendResponse(struct sq_connection* 
connection,
                     mode == StyleMode::STYLED ? "text/html" : "text/plain");
   oss << Substitute("Content-Length: $0\r\n", body.length());
   if (is_compressed) oss << "Content-Encoding: gzip\r\n";
+  if (PREDICT_TRUE(FLAGS_webserver_enable_csp)) {
+    // TODO(aserbin): add information on when to update the SHA hash and
+    //                how to do so (ideally, the exact command line)
+    oss << "Content-Security-Policy: default-src 'self';"
+        << "style-src 'self' 'unsafe-hashes' 
'sha256-47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=';"
+        << "img-src 'self' data:;\r\n";
+  }
   oss << Substitute("X-Frame-Options: $0\r\n", 
FLAGS_webserver_x_frame_options);
   static const unordered_set<string> kInvalidHeaders = {
     "Content-Length",
     "Content-Type",
+    "Content-Security-Policy",
     "X-Frame-Options"
   };
   for (const auto& entry : resp->response_headers) {
@@ -771,7 +784,7 @@ static const char* const kMainTemplate = R"(
     <nav class="navbar navbar-default">
       <div class="container-fluid">
         <div class="navbar-header">
-          <a class="navbar-brand" style="padding-top: 5px;" 
href="{{base_url}}/">
+          <a class="navbar-brand" href="{{base_url}}/">
             <img src="{{base_url}}/logo.png" width='61' height='45' 
alt="Kudu"/>
           </a>
         </div>
diff --git a/www/kudu.css b/www/kudu.css
index d44905ff9..4de8d5d3a 100644
--- a/www/kudu.css
+++ b/www/kudu.css
@@ -53,3 +53,15 @@ body {
 .glyphicon-hide {
   color: transparent;
 }
+
+.navbar-brand {
+  padding-top: 5px;
+}
+
+tr.startup-header {
+  background-color: #eee !important;
+}
+
+td.startup-indented {
+  padding-left: 50px !important;
+}
diff --git a/www/startup.mustache b/www/startup.mustache
index fb026eee9..c5b0cce9d 100644
--- a/www/startup.mustache
+++ b/www/startup.mustache
@@ -22,7 +22,7 @@ under the License.
 {{^error}}
   <h2>Server Startup Progress</h2>
     <table class='table table-striped'>
-      <tr style="background-color: #eee">
+      <tr class="startup-header">
         <th>Step</th>
         <th>Progress (%)</th>
         <th>Elapsed Time</th>
@@ -38,20 +38,20 @@ under the License.
         <th>{{read_filesystem_time}}</th>
       </tr>
       <tr>
-        <td style="padding-left:50px">Reading instance metadata files</td>
+        <td class="startup-indented">Reading instance metadata files</td>
         <td>{{read_instance_metadatafiles_status}}%</td>
         <td>{{read_instance_metadatafiles_time}}</td>
       </tr>
       {{#is_log_block_manager}}
         <tr>
-          <td style="padding-left:50px">Opening container files&emsp; 
&emsp;({{containers_processed}}/{{containers_total}})</td>
+          <td class="startup-indented">Opening container files&emsp; 
&emsp;({{containers_processed}}/{{containers_total}})</td>
           <td>{{read_data_directories_status}}%</td>
           <td>{{read_data_directories_time}}</td>
         </tr>
       {{/is_log_block_manager}}
       {{^is_log_block_manager}}
         <tr>
-          <td style="padding-left:50px">Reporting filesystem</td>
+          <td class="startup-indented">Reporting filesystem</td>
           <td>{{read_data_directories_status}}%</td>
           <td>{{read_data_directories_time}}</td>
         </tr>

Reply via email to