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 
 ({{containers_processed}}/{{containers_total}})</td>
+ <td class="startup-indented">Opening container files 
 ({{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>