You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@knox.apache.org by lm...@apache.org on 2016/03/29 05:02:41 UTC
knox git commit: KNOX-700 - Add Clickjacking Protection to WebAppSec
Provider
Repository: knox
Updated Branches:
refs/heads/master 61b4f8d24 -> 69cbfa587
KNOX-700 - Add Clickjacking Protection to WebAppSec Provider
Project: http://git-wip-us.apache.org/repos/asf/knox/repo
Commit: http://git-wip-us.apache.org/repos/asf/knox/commit/69cbfa58
Tree: http://git-wip-us.apache.org/repos/asf/knox/tree/69cbfa58
Diff: http://git-wip-us.apache.org/repos/asf/knox/diff/69cbfa58
Branch: refs/heads/master
Commit: 69cbfa58705def7f80e0a31e05924505f8b2811e
Parents: 61b4f8d
Author: Larry McCay <lm...@hortonworks.com>
Authored: Mon Mar 28 23:02:30 2016 -0400
Committer: Larry McCay <lm...@hortonworks.com>
Committed: Mon Mar 28 23:02:30 2016 -0400
----------------------------------------------------------------------
.../webappsec/deploy/WebAppSecContributor.java | 30 ++-
.../webappsec/filter/XFrameOptionsFilter.java | 137 +++++++++++++
.../gateway/provider/federation/CSRFTest.java | 30 ---
.../hadoop/gateway/webappsec/CSRFTest.java | 29 +++
.../webappsec/XFrameOptionsFilterTest.java | 193 +++++++++++++++++++
5 files changed, 383 insertions(+), 36 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/knox/blob/69cbfa58/gateway-provider-security-webappsec/src/main/java/org/apache/hadoop/gateway/webappsec/deploy/WebAppSecContributor.java
----------------------------------------------------------------------
diff --git a/gateway-provider-security-webappsec/src/main/java/org/apache/hadoop/gateway/webappsec/deploy/WebAppSecContributor.java b/gateway-provider-security-webappsec/src/main/java/org/apache/hadoop/gateway/webappsec/deploy/WebAppSecContributor.java
index 86cf899..50a6767 100644
--- a/gateway-provider-security-webappsec/src/main/java/org/apache/hadoop/gateway/webappsec/deploy/WebAppSecContributor.java
+++ b/gateway-provider-security-webappsec/src/main/java/org/apache/hadoop/gateway/webappsec/deploy/WebAppSecContributor.java
@@ -39,6 +39,9 @@ public class WebAppSecContributor extends
private static final String CORS_SUFFIX = "_CORS";
private static final String CORS_FILTER_CLASSNAME = "com.thetransactioncompany.cors.CORSFilter";
private static final String CORS_ENABLED = "cors.enabled";
+ private static final String XFRAME_OPTIONS_SUFFIX = "_XFRAMEOPTIONS";
+ private static final String XFRAME_OPTIONS_FILTER_CLASSNAME = "org.apache.hadoop.gateway.webappsec.filter.XFrameOptionsFilter";
+ private static final String XFRAME_OPTIONS_ENABLED = "xframe.options.enabled";
@Override
public String getRole() {
@@ -62,27 +65,42 @@ public class WebAppSecContributor extends
Provider webappsec = context.getTopology().getProvider(ROLE, NAME);
if (webappsec != null && webappsec.isEnabled()) {
Map<String,String> map = provider.getParams();
- String csrfEnabled = map.get(CSRF_ENABLED);
if (params == null) {
params = new ArrayList<FilterParamDescriptor>();
}
- // blindly add all the provider params as filter init params
- Map<String, String> providerParams = provider.getParams();
- for(Entry<String, String> entry : providerParams.entrySet()) {
- params.add( resource.createFilterParam().name( entry.getKey().toLowerCase() ).value( entry.getValue() ) );
- }
+ Map<String, String> providerParams = provider.getParams();
// CORS support
String corsEnabled = map.get(CORS_ENABLED);
if ( corsEnabled != null && corsEnabled.equals("true")) {
+ provisionConfig(resource, providerParams, params, "cors.");
resource.addFilter().name( getName() + CORS_SUFFIX ).role( getRole() ).impl( CORS_FILTER_CLASSNAME ).params( params );
}
// CRSF
+ params = new ArrayList<FilterParamDescriptor>();
+ String csrfEnabled = map.get(CSRF_ENABLED);
if ( csrfEnabled != null && csrfEnabled.equals("true")) {
+ provisionConfig(resource, providerParams, params, "csrf.");
resource.addFilter().name( getName() + CSRF_SUFFIX ).role( getRole() ).impl( CSRF_FILTER_CLASSNAME ).params( params );
}
+ // X-Frame-Options - clickjacking protection
+ params = new ArrayList<FilterParamDescriptor>();
+ String xframeOptionsEnabled = map.get(XFRAME_OPTIONS_ENABLED);
+ if ( xframeOptionsEnabled != null && xframeOptionsEnabled.equals("true")) {
+ provisionConfig(resource, providerParams, params, "xframe.");
+ resource.addFilter().name( getName() + XFRAME_OPTIONS_SUFFIX ).role( getRole() ).impl( XFRAME_OPTIONS_FILTER_CLASSNAME ).params( params );
+ }
+ }
+ }
+
+ private void provisionConfig(ResourceDescriptor resource, Map<String,String> providerParams,
+ List<FilterParamDescriptor> params, String prefix) {
+ for(Entry<String, String> entry : providerParams.entrySet()) {
+ if (entry.getKey().startsWith(prefix)) {
+ params.add( resource.createFilterParam().name( entry.getKey().toLowerCase() ).value( entry.getValue() ) );
+ }
}
}
}
http://git-wip-us.apache.org/repos/asf/knox/blob/69cbfa58/gateway-provider-security-webappsec/src/main/java/org/apache/hadoop/gateway/webappsec/filter/XFrameOptionsFilter.java
----------------------------------------------------------------------
diff --git a/gateway-provider-security-webappsec/src/main/java/org/apache/hadoop/gateway/webappsec/filter/XFrameOptionsFilter.java b/gateway-provider-security-webappsec/src/main/java/org/apache/hadoop/gateway/webappsec/filter/XFrameOptionsFilter.java
new file mode 100644
index 0000000..9ec14a6
--- /dev/null
+++ b/gateway-provider-security-webappsec/src/main/java/org/apache/hadoop/gateway/webappsec/filter/XFrameOptionsFilter.java
@@ -0,0 +1,137 @@
+/**
+ * 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.gateway.webappsec.filter;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+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 javax.servlet.http.HttpServletResponseWrapper;
+
+/**
+ * This filter protects proxied webapps from clickjacking attacks that
+ * are possible through use of Frames to contain the proxied resources.
+ */
+public class XFrameOptionsFilter implements Filter {
+ private static final String X_FRAME_OPTIONS = "X-Frame-Options";
+ private static final String CUSTOM_HEADER_PARAM = "xframe.options";
+
+ private String option = "DENY";
+
+ /* (non-Javadoc)
+ * @see javax.servlet.Filter#destroy()
+ */
+ @Override
+ public void destroy() {
+ }
+
+ /* (non-Javadoc)
+ * @see javax.servlet.Filter#doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain)
+ */
+ @Override
+ public void doFilter(ServletRequest req, ServletResponse res,
+ FilterChain chain) throws IOException, ServletException {
+ ((HttpServletResponse) res).setHeader(X_FRAME_OPTIONS, option);
+ chain.doFilter(req, new XFrameOptionsResponseWrapper((HttpServletResponse) res));
+ }
+
+ /* (non-Javadoc)
+ * @see javax.servlet.Filter#init(javax.servlet.FilterConfig)
+ */
+ @Override
+ public void init(FilterConfig config) throws ServletException {
+ String customOption = config.getInitParameter(CUSTOM_HEADER_PARAM);
+ if (customOption != null) {
+ option = customOption;
+ }
+ }
+
+ public class XFrameOptionsResponseWrapper extends HttpServletResponseWrapper {
+ @Override
+ public void addHeader(String name, String value) {
+ // don't allow additional values to be added to
+ // the configured options value in topology
+ if (!name.equals(X_FRAME_OPTIONS)) {
+ super.addHeader(name, value);
+ }
+ }
+
+ @Override
+ public void setHeader(String name, String value) {
+ // don't allow overwriting of configured value
+ if (!name.equals(X_FRAME_OPTIONS)) {
+ super.setHeader(name, value);
+ }
+ }
+
+ /**
+ * construct a wrapper for this request
+ *
+ * @param request
+ */
+ public XFrameOptionsResponseWrapper(HttpServletResponse response) {
+ super(response);
+ }
+
+ @Override
+ public String getHeader(String name) {
+ String headerValue = null;
+ if (name.equals(X_FRAME_OPTIONS)) {
+ headerValue = option;
+ }
+ else {
+ headerValue = super.getHeader(name);
+ }
+ return headerValue;
+ }
+
+ /**
+ * get the Header names
+ */
+ @Override
+ public Collection<String> getHeaderNames() {
+ List<String> names = (List<String>) super.getHeaderNames();
+ if (names == null) {
+ names = new ArrayList<String>();
+ }
+ names.add(X_FRAME_OPTIONS);
+ return names;
+ }
+
+ @Override
+ public Collection<String> getHeaders(String name) {
+ List<String> values = (List<String>) super.getHeaders(name);
+ if (name.equals(X_FRAME_OPTIONS)) {
+ if (values == null) {
+ values = new ArrayList<String>();
+ }
+ values.add(option);
+ }
+ return values;
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/knox/blob/69cbfa58/gateway-provider-security-webappsec/src/test/java/org/apache/hadoop/gateway/provider/federation/CSRFTest.java
----------------------------------------------------------------------
diff --git a/gateway-provider-security-webappsec/src/test/java/org/apache/hadoop/gateway/provider/federation/CSRFTest.java b/gateway-provider-security-webappsec/src/test/java/org/apache/hadoop/gateway/provider/federation/CSRFTest.java
deleted file mode 100644
index 28ec021..0000000
--- a/gateway-provider-security-webappsec/src/test/java/org/apache/hadoop/gateway/provider/federation/CSRFTest.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/**
- * 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.gateway.provider.federation;
-
-import junit.framework.TestCase;
-
-import org.apache.hadoop.gateway.services.security.token.impl.JWTToken;
-import org.junit.Test;
-
-public class CSRFTest extends TestCase {
- @Test
- public void testCsrf() throws Exception {
- assertTrue(true);
- }
-}
http://git-wip-us.apache.org/repos/asf/knox/blob/69cbfa58/gateway-provider-security-webappsec/src/test/java/org/apache/hadoop/gateway/webappsec/CSRFTest.java
----------------------------------------------------------------------
diff --git a/gateway-provider-security-webappsec/src/test/java/org/apache/hadoop/gateway/webappsec/CSRFTest.java b/gateway-provider-security-webappsec/src/test/java/org/apache/hadoop/gateway/webappsec/CSRFTest.java
new file mode 100644
index 0000000..baab4b2
--- /dev/null
+++ b/gateway-provider-security-webappsec/src/test/java/org/apache/hadoop/gateway/webappsec/CSRFTest.java
@@ -0,0 +1,29 @@
+/**
+ * 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.gateway.webappsec;
+
+import junit.framework.TestCase;
+
+import org.junit.Test;
+
+public class CSRFTest extends TestCase {
+ @Test
+ public void testCsrf() throws Exception {
+ assertTrue(true);
+ }
+}
http://git-wip-us.apache.org/repos/asf/knox/blob/69cbfa58/gateway-provider-security-webappsec/src/test/java/org/apache/hadoop/gateway/webappsec/XFrameOptionsFilterTest.java
----------------------------------------------------------------------
diff --git a/gateway-provider-security-webappsec/src/test/java/org/apache/hadoop/gateway/webappsec/XFrameOptionsFilterTest.java b/gateway-provider-security-webappsec/src/test/java/org/apache/hadoop/gateway/webappsec/XFrameOptionsFilterTest.java
new file mode 100644
index 0000000..cda73f6
--- /dev/null
+++ b/gateway-provider-security-webappsec/src/test/java/org/apache/hadoop/gateway/webappsec/XFrameOptionsFilterTest.java
@@ -0,0 +1,193 @@
+/**
+ * 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.gateway.webappsec;
+
+import static org.junit.Assert.fail;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.Properties;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.hadoop.gateway.webappsec.filter.XFrameOptionsFilter;
+import org.easymock.EasyMock;
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ *
+ */
+public class XFrameOptionsFilterTest {
+ /**
+ *
+ */
+ private static final String X_FRAME_OPTIONS = "X-Frame-Options";
+ String options = null;
+ Collection<String> headerNames = null;
+ Collection<String> headers = null;
+
+ @Test
+ public void testDefaultOptionsValue() throws Exception {
+ try {
+ XFrameOptionsFilter filter = new XFrameOptionsFilter();
+ Properties props = new Properties();
+ props.put("xframe.options.enabled", "true");
+ filter.init(new TestFilterConfig(props));
+
+ HttpServletRequest request = EasyMock.createNiceMock(
+ HttpServletRequest.class);
+ HttpServletResponse response = EasyMock.createNiceMock(
+ HttpServletResponse.class);
+ EasyMock.replay(request);
+ EasyMock.replay(response);
+
+ TestFilterChain chain = new TestFilterChain();
+ filter.doFilter(request, response, chain);
+ Assert.assertTrue("doFilterCalled should not be false.",
+ chain.doFilterCalled == true);
+ Assert.assertTrue("Options value incorrect should be DENY but is: "
+ + options, options.equals("DENY"));
+
+ Assert.assertTrue("X-Frame-Options count not equal to 1.", headers.size() == 1);
+ } catch (ServletException se) {
+ fail("Should NOT have thrown a ServletException.");
+ }
+ }
+
+ @Test
+ public void testConfiguredOptionsValue() throws Exception {
+ try {
+ XFrameOptionsFilter filter = new XFrameOptionsFilter();
+ Properties props = new Properties();
+ props.put("xframe.options.enabled", "true");
+ props.put("xframe.options", "SAMEORIGIN");
+ filter.init(new TestFilterConfig(props));
+
+ HttpServletRequest request = EasyMock.createNiceMock(
+ HttpServletRequest.class);
+ HttpServletResponse response = EasyMock.createNiceMock(
+ HttpServletResponse.class);
+ EasyMock.replay(request);
+ EasyMock.replay(response);
+
+ TestFilterChain chain = new TestFilterChain();
+ filter.doFilter(request, response, chain);
+ Assert.assertTrue("doFilterCalled should not be false.",
+ chain.doFilterCalled == true);
+ Assert.assertTrue("Options value incorrect should be SAMEORIGIN but is: "
+ + options, options.equals("SAMEORIGIN"));
+
+ Assert.assertTrue("X-Frame-Options count not equal to 1.", headers.size() == 1);
+ } catch (ServletException se) {
+ fail("Should NOT have thrown a ServletException.");
+ }
+ }
+
+// @Test
+// public void testExistingXFrameOptionHeader() throws Exception {
+// try {
+// XFrameOptionsFilter filter = new XFrameOptionsFilter();
+// Properties props = new Properties();
+// props.put("xframe.options.enabled", "true");
+// props.put("xframe.options", "SAMEORIGIN");
+// filter.init(new TestFilterConfig(props));
+//
+// HttpServletRequest request = EasyMock.createNiceMock(
+// HttpServletRequest.class);
+// HttpServletResponse response = EasyMock.createNiceMock(
+// HttpServletResponse.class);
+// EasyMock.replay(request);
+// EasyMock.replay(response);
+//
+// TestFilterChain chain = new TestFilterChain();
+// filter.doFilter(request, response, chain);
+// Assert.assertTrue("doFilterCalled should not be false.",
+// chain.doFilterCalled == true);
+// Assert.assertTrue("Options value incorrect should be SAMEORIGIN but is: "
+// + options, options.equals("SAMEORIGIN"));
+//
+// Assert.assertTrue("X-Frame-Options count not equal to 1.", headers.size() == 1);
+// } catch (ServletException se) {
+// fail("Should NOT have thrown a ServletException.");
+// }
+// }
+
+ class TestFilterConfig implements FilterConfig {
+ Properties props = null;
+
+ public TestFilterConfig(Properties props) {
+ this.props = props;
+ }
+
+ @Override
+ public String getFilterName() {
+ return null;
+ }
+
+ /* (non-Javadoc)
+ * @see javax.servlet.FilterConfig#getServletContext()
+ */
+ @Override
+ public ServletContext getServletContext() {
+ return null;
+ }
+
+ /* (non-Javadoc)
+ * @see javax.servlet.FilterConfig#getInitParameter(java.lang.String)
+ */
+ @Override
+ public String getInitParameter(String name) {
+ return props.getProperty(name, null);
+ }
+
+ /* (non-Javadoc)
+ * @see javax.servlet.FilterConfig#getInitParameterNames()
+ */
+ @Override
+ public Enumeration<String> getInitParameterNames() {
+ return null;
+ }
+
+ }
+
+ class TestFilterChain implements FilterChain {
+ boolean doFilterCalled = false;
+
+ /* (non-Javadoc)
+ * @see javax.servlet.FilterChain#doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse)
+ */
+ @Override
+ public void doFilter(ServletRequest request, ServletResponse response)
+ throws IOException, ServletException {
+ doFilterCalled = true;
+ options = ((HttpServletResponse)response).getHeader(X_FRAME_OPTIONS);
+ headerNames = ((HttpServletResponse)response).getHeaderNames();
+ headers = ((HttpServletResponse)response).getHeaders(X_FRAME_OPTIONS);
+ }
+
+ }
+
+}