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

meszibalu pushed a commit to branch branch-2.5
in repository https://gitbox.apache.org/repos/asf/hbase.git


The following commit(s) were added to refs/heads/branch-2.5 by this push:
     new 6aa32f0  HBASE-23303 Add default security headers if SSL is enabled 
(#4128)
6aa32f0 is described below

commit 6aa32f03a8d13d667a6e4660a12eb08acce13211
Author: Andor Molnár <[email protected]>
AuthorDate: Wed Mar 2 15:25:57 2022 +0100

    HBASE-23303 Add default security headers if SSL is enabled (#4128)
    
    Signed-off-by: Balazs Meszaros <[email protected]>
---
 .../main/java/org/apache/hadoop/hbase/http/HttpServer.java |  4 +++-
 .../apache/hadoop/hbase/http/SecurityHeadersFilter.java    | 12 +++++-------
 .../org/apache/hadoop/hbase/http/TestSSLHttpServer.java    | 14 ++++++++++++++
 .../main/java/org/apache/hadoop/hbase/rest/RESTServer.java |  9 ++++++---
 .../org/apache/hadoop/hbase/rest/TestRESTServerSSL.java    |  8 ++++++--
 5 files changed, 34 insertions(+), 13 deletions(-)

diff --git 
a/hbase-http/src/main/java/org/apache/hadoop/hbase/http/HttpServer.java 
b/hbase-http/src/main/java/org/apache/hadoop/hbase/http/HttpServer.java
index d534e15..884780c 100644
--- a/hbase-http/src/main/java/org/apache/hadoop/hbase/http/HttpServer.java
+++ b/hbase-http/src/main/java/org/apache/hadoop/hbase/http/HttpServer.java
@@ -626,9 +626,11 @@ public class HttpServer implements FilterContainer {
         ClickjackingPreventionFilter.class.getName(),
         ClickjackingPreventionFilter.getDefaultParameters(conf));
 
+    HttpConfig httpConfig = new HttpConfig(conf);
+
     addGlobalFilter("securityheaders",
         SecurityHeadersFilter.class.getName(),
-        SecurityHeadersFilter.getDefaultParameters(conf));
+        SecurityHeadersFilter.getDefaultParameters(conf, 
httpConfig.isSecure()));
 
     // But security needs to be enabled prior to adding the other servlets
     if (authenticationEnabled) {
diff --git 
a/hbase-http/src/main/java/org/apache/hadoop/hbase/http/SecurityHeadersFilter.java
 
b/hbase-http/src/main/java/org/apache/hadoop/hbase/http/SecurityHeadersFilter.java
index b83fef1..f00f2a1 100644
--- 
a/hbase-http/src/main/java/org/apache/hadoop/hbase/http/SecurityHeadersFilter.java
+++ 
b/hbase-http/src/main/java/org/apache/hadoop/hbase/http/SecurityHeadersFilter.java
@@ -39,8 +39,8 @@ import org.slf4j.LoggerFactory;
 public class SecurityHeadersFilter implements Filter {
   private static final Logger LOG =
       LoggerFactory.getLogger(SecurityHeadersFilter.class);
-  private static final String DEFAULT_HSTS = "";
-  private static final String DEFAULT_CSP = "";
+  private static final String DEFAULT_HSTS = 
"max-age=63072000;includeSubDomains;preload";
+  private static final String DEFAULT_CSP = "default-src https: data: 
'unsafe-inline' 'unsafe-eval'";
   private FilterConfig filterConfig;
 
   @Override
@@ -70,12 +70,10 @@ public class SecurityHeadersFilter implements Filter {
   public void destroy() {
   }
 
-  public static Map<String, String> getDefaultParameters(Configuration conf) {
+  public static Map<String, String> getDefaultParameters(Configuration conf, 
boolean isSecure) {
     Map<String, String> params = new HashMap<>();
-    params.put("hsts", conf.get("hbase.http.filter.hsts.value",
-        DEFAULT_HSTS));
-    params.put("csp", conf.get("hbase.http.filter.csp.value",
-        DEFAULT_CSP));
+    params.put("hsts", conf.get("hbase.http.filter.hsts.value", isSecure ? 
DEFAULT_HSTS : ""));
+    params.put("csp", conf.get("hbase.http.filter.csp.value", isSecure ? 
DEFAULT_CSP : ""));
     return params;
   }
 }
diff --git 
a/hbase-http/src/test/java/org/apache/hadoop/hbase/http/TestSSLHttpServer.java 
b/hbase-http/src/test/java/org/apache/hadoop/hbase/http/TestSSLHttpServer.java
index 382118d..2b72793 100644
--- 
a/hbase-http/src/test/java/org/apache/hadoop/hbase/http/TestSSLHttpServer.java
+++ 
b/hbase-http/src/test/java/org/apache/hadoop/hbase/http/TestSSLHttpServer.java
@@ -19,9 +19,11 @@ package org.apache.hadoop.hbase.http;
 
 import java.io.ByteArrayOutputStream;
 import java.io.File;
+import java.io.IOException;
 import java.io.InputStream;
 import java.net.URI;
 import java.net.URL;
+import java.security.GeneralSecurityException;
 import javax.net.ssl.HttpsURLConnection;
 import org.apache.hadoop.conf.Configuration;
 import org.apache.hadoop.fs.FileUtil;
@@ -73,6 +75,7 @@ public class TestSSLHttpServer extends 
HttpServerFunctionalTest {
     serverConf = HTU.getConfiguration();
 
     serverConf.setInt(HttpServer.HTTP_MAX_THREADS, TestHttpServer.MAX_THREADS);
+    serverConf.setBoolean(ServerConfigurationKeys.HBASE_SSL_ENABLED_KEY, true);
 
     keystoresDir = new File(HTU.getDataTestDir("keystore").toString());
     keystoresDir.mkdirs();
@@ -122,6 +125,17 @@ public class TestSSLHttpServer extends 
HttpServerFunctionalTest {
         "/echo?a=b&c<=d&e=>")));
   }
 
+  @Test
+  public void testSecurityHeaders() throws IOException, 
GeneralSecurityException {
+    HttpsURLConnection conn = (HttpsURLConnection) baseUrl.openConnection();
+    conn.setSSLSocketFactory(clientSslFactory.createSSLSocketFactory());
+    assertEquals(HttpsURLConnection.HTTP_OK, conn.getResponseCode());
+    assertEquals("max-age=63072000;includeSubDomains;preload",
+      conn.getHeaderField("Strict-Transport-Security"));
+    assertEquals("default-src https: data: 'unsafe-inline' 'unsafe-eval'",
+      conn.getHeaderField("Content-Security-Policy"));
+  }
+
   private static String readOut(URL url) throws Exception {
     HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
     conn.setSSLSocketFactory(clientSslFactory.createSSLSocketFactory());
diff --git 
a/hbase-rest/src/main/java/org/apache/hadoop/hbase/rest/RESTServer.java 
b/hbase-rest/src/main/java/org/apache/hadoop/hbase/rest/RESTServer.java
index 9ceef8c..3a944c0 100644
--- a/hbase-rest/src/main/java/org/apache/hadoop/hbase/rest/RESTServer.java
+++ b/hbase-rest/src/main/java/org/apache/hadoop/hbase/rest/RESTServer.java
@@ -147,11 +147,12 @@ public class RESTServer implements Constants {
     ctxHandler.addFilter(holder, PATH_SPEC_ANY, 
EnumSet.allOf(DispatcherType.class));
   }
 
-  private void addSecurityHeadersFilter(ServletContextHandler ctxHandler, 
Configuration conf) {
+  private void addSecurityHeadersFilter(ServletContextHandler ctxHandler,
+    Configuration conf, boolean isSecure) {
     FilterHolder holder = new FilterHolder();
     holder.setName("securityheaders");
     holder.setClassName(SecurityHeadersFilter.class.getName());
-    holder.setInitParameters(SecurityHeadersFilter.getDefaultParameters(conf));
+    holder.setInitParameters(SecurityHeadersFilter.getDefaultParameters(conf, 
isSecure));
     ctxHandler.addFilter(holder, PATH_SPEC_ANY, 
EnumSet.allOf(DispatcherType.class));
   }
 
@@ -304,7 +305,9 @@ public class RESTServer implements Constants {
     httpConfig.setSendDateHeader(false);
 
     ServerConnector serverConnector;
+    boolean isSecure = false;
     if (conf.getBoolean(REST_SSL_ENABLED, false)) {
+      isSecure = true;
       HttpConfiguration httpsConfig = new HttpConfiguration(httpConfig);
       httpsConfig.addCustomizer(new SecureRequestCustomizer());
 
@@ -392,7 +395,7 @@ public class RESTServer implements Constants {
     }
     addCSRFFilter(ctxHandler, conf);
     addClickjackingPreventionFilter(ctxHandler, conf);
-    addSecurityHeadersFilter(ctxHandler, conf);
+    addSecurityHeadersFilter(ctxHandler, conf, isSecure);
     HttpServerUtil.constrainHttpMethods(ctxHandler, servlet.getConfiguration()
         .getBoolean(REST_HTTP_ALLOW_OPTIONS_METHOD, 
REST_HTTP_ALLOW_OPTIONS_METHOD_DEFAULT));
 
diff --git 
a/hbase-rest/src/test/java/org/apache/hadoop/hbase/rest/TestRESTServerSSL.java 
b/hbase-rest/src/test/java/org/apache/hadoop/hbase/rest/TestRESTServerSSL.java
index ea13360..1a55996 100644
--- 
a/hbase-rest/src/test/java/org/apache/hadoop/hbase/rest/TestRESTServerSSL.java
+++ 
b/hbase-rest/src/test/java/org/apache/hadoop/hbase/rest/TestRESTServerSSL.java
@@ -18,8 +18,6 @@
 package org.apache.hadoop.hbase.rest;
 
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-
 import java.io.File;
 import java.security.KeyPair;
 import java.security.cert.X509Certificate;
@@ -107,6 +105,12 @@ public class TestRESTServerSSL {
 
     Response response = sslClient.get("/version", Constants.MIMETYPE_TEXT);
     assertEquals(200, response.getCode());
+
+    // Default security headers
+    assertEquals("max-age=63072000;includeSubDomains;preload",
+      response.getHeader("Strict-Transport-Security"));
+    assertEquals("default-src https: data: 'unsafe-inline' 'unsafe-eval'",
+      response.getHeader("Content-Security-Policy"));
   }
 
   @Test(expected = org.apache.http.client.ClientProtocolException.class)

Reply via email to