From dc9c2efcc9a7d0f00d738b1b7dbd59a208c5a7a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1bor=20Lipt=C3=A1k?= Date: Fri, 31 Jul 2015 20:34:24 -0400 Subject: [PATCH] HBASE-14148 Default HBase web pages to be non-framable. * Sends X-Frame-Options header * configured via hbase.http.filter.xframeoptions.mode * defaults to DENY Signed-off-by: Sean Busbey --- .../http/ClickjackingPreventionFilter.java | 55 +++++++++++++++++++ .../apache/hadoop/hbase/http/HttpServer.java | 4 ++ .../hadoop/hbase/http/TestHttpServer.java | 24 ++++++++ 3 files changed, 83 insertions(+) create mode 100644 hbase-server/src/main/java/org/apache/hadoop/hbase/http/ClickjackingPreventionFilter.java diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/http/ClickjackingPreventionFilter.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/http/ClickjackingPreventionFilter.java new file mode 100644 index 00000000000..5e786fc23d6 --- /dev/null +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/http/ClickjackingPreventionFilter.java @@ -0,0 +1,55 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ +package org.apache.hadoop.hbase.http; + +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +import org.apache.hadoop.hbase.classification.InterfaceAudience; +import org.apache.hadoop.hbase.HBaseInterfaceAudience; + +@InterfaceAudience.LimitedPrivate(HBaseInterfaceAudience.CONFIG) +public class ClickjackingPreventionFilter implements Filter { + + private FilterConfig filterConfig; + + @Override + public void init(FilterConfig filterConfig) throws ServletException { + this.filterConfig = filterConfig; + } + + @Override + public void doFilter(ServletRequest req, ServletResponse res, + FilterChain chain) + throws IOException, ServletException { + HttpServletResponse httpRes = (HttpServletResponse) res; + httpRes.addHeader("X-Frame-Options", filterConfig.getInitParameter("xframeoptions")); + chain.doFilter(req, res); + } + + @Override + public void destroy() { + } + +} diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/http/HttpServer.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/http/HttpServer.java index 29a2c515552..667e597ca86 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/http/HttpServer.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/http/HttpServer.java @@ -533,6 +533,10 @@ public class HttpServer implements FilterContainer { addDefaultApps(contexts, appDir, conf); addGlobalFilter("safety", QuotingInputFilter.class.getName(), null); + Map params = new HashMap(); + params.put("xframeoptions", conf.get("hbase.http.filter.xframeoptions.mode", "DENY")); + addGlobalFilter("clickjackingprevention", + ClickjackingPreventionFilter.class.getName(), params); final FilterInitializer[] initializers = getFilterInitializers(conf); if (initializers != null) { conf = new Configuration(conf); diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/http/TestHttpServer.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/http/TestHttpServer.java index 7adf18a7e83..6642638cba5 100644 --- a/hbase-server/src/test/java/org/apache/hadoop/hbase/http/TestHttpServer.java +++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/http/TestHttpServer.java @@ -588,6 +588,29 @@ public class TestHttpServer extends HttpServerFunctionalTest { return server; } + @Test + public void testXFrameHeaderSameOrigin() throws Exception { + Configuration conf = new Configuration(); + conf.set("hbase.http.filter.xframeoptions.mode", "SAMEORIGIN"); + + HttpServer myServer = new HttpServer.Builder().setName("test") + .addEndpoint(new URI("http://localhost:0")) + .setFindPort(true).setConf(conf).build(); + myServer.setAttribute(HttpServer.CONF_CONTEXT_ATTRIBUTE, conf); + myServer.addServlet("echo", "/echo", EchoServlet.class); + myServer.start(); + + String serverURL = "http://" + + NetUtils.getHostPortString(myServer.getConnectorAddress(0)); + URL url = new URL(new URL(serverURL), "/echo?a=b&c=d"); + HttpURLConnection conn = (HttpURLConnection) url.openConnection(); + assertEquals(HttpURLConnection.HTTP_OK, conn.getResponseCode()); + assertEquals("SAMEORIGIN", conn.getHeaderField("X-Frame-Options")); + myServer.stop(); + } + + + @Test public void testNoCacheHeader() throws Exception { URL url = new URL(baseUrl, "/echo?a=b&c=d"); @@ -598,6 +621,7 @@ public class TestHttpServer extends HttpServerFunctionalTest { assertNotNull(conn.getHeaderField("Expires")); assertNotNull(conn.getHeaderField("Date")); assertEquals(conn.getHeaderField("Expires"), conn.getHeaderField("Date")); + assertEquals("DENY", conn.getHeaderField("X-Frame-Options")); } /**