You are viewing a plain text version of this content. The canonical link for it is here.
Posted to common-commits@hadoop.apache.org by as...@apache.org on 2016/02/11 08:59:42 UTC
[08/50] hadoop git commit: HADOOP-12758. Extend CSRF Filter with
UserAgent Checks. Contributed by Larry McCay.
HADOOP-12758. Extend CSRF Filter with UserAgent Checks. Contributed by Larry McCay.
Project: http://git-wip-us.apache.org/repos/asf/hadoop/repo
Commit: http://git-wip-us.apache.org/repos/asf/hadoop/commit/a37e423e
Tree: http://git-wip-us.apache.org/repos/asf/hadoop/tree/a37e423e
Diff: http://git-wip-us.apache.org/repos/asf/hadoop/diff/a37e423e
Branch: refs/heads/yarn-2877
Commit: a37e423e8407c42988577d87907d13ce0432dda1
Parents: 49e176c
Author: cnauroth <cn...@apache.org>
Authored: Fri Feb 5 14:38:21 2016 -0800
Committer: cnauroth <cn...@apache.org>
Committed: Fri Feb 5 14:38:21 2016 -0800
----------------------------------------------------------------------
hadoop-common-project/hadoop-common/CHANGES.txt | 3 +
.../security/http/RestCsrfPreventionFilter.java | 54 +++++++++++-
.../http/TestRestCsrfPreventionFilter.java | 93 ++++++++++++++++++--
3 files changed, 142 insertions(+), 8 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/hadoop/blob/a37e423e/hadoop-common-project/hadoop-common/CHANGES.txt
----------------------------------------------------------------------
diff --git a/hadoop-common-project/hadoop-common/CHANGES.txt b/hadoop-common-project/hadoop-common/CHANGES.txt
index 13568e2..3fdce4a 100644
--- a/hadoop-common-project/hadoop-common/CHANGES.txt
+++ b/hadoop-common-project/hadoop-common/CHANGES.txt
@@ -1137,6 +1137,9 @@ Release 2.8.0 - UNRELEASED
HADOOP-12450. UserGroupInformation should not log at WARN level if no groups
are found. (Elliott Clark via stevel)
+ HADOOP-12758. Extend CSRF Filter with UserAgent Checks
+ (Larry McCay via cnauroth)
+
BUG FIXES
HADOOP-12352. Delay in checkpointing Trash can leave trash for 2 intervals
http://git-wip-us.apache.org/repos/asf/hadoop/blob/a37e423e/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/http/RestCsrfPreventionFilter.java
----------------------------------------------------------------------
diff --git a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/http/RestCsrfPreventionFilter.java b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/http/RestCsrfPreventionFilter.java
index 50f95ad..4f7f5bb 100644
--- a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/http/RestCsrfPreventionFilter.java
+++ b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/http/RestCsrfPreventionFilter.java
@@ -19,6 +19,8 @@ package org.apache.hadoop.security.http;
import java.io.IOException;
import java.util.HashSet;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
import java.util.Set;
import javax.servlet.Filter;
@@ -38,13 +40,18 @@ import javax.servlet.http.HttpServletResponse;
* attempt as a bad request.
*/
public class RestCsrfPreventionFilter implements Filter {
+ public static final String HEADER_USER_AGENT = "User-Agent";
+ public static final String BROWSER_USER_AGENT_PARAM =
+ "browser-useragents-regex";
public static final String CUSTOM_HEADER_PARAM = "custom-header";
public static final String CUSTOM_METHODS_TO_IGNORE_PARAM =
"methods-to-ignore";
+ static final String BROWSER_USER_AGENTS_DEFAULT = "^Mozilla.*,^Opera.*";
static final String HEADER_DEFAULT = "X-XSRF-HEADER";
static final String METHODS_TO_IGNORE_DEFAULT = "GET,OPTIONS,HEAD,TRACE";
private String headerName = HEADER_DEFAULT;
private Set<String> methodsToIgnore = null;
+ private Set<Pattern> browserUserAgents;
@Override
public void init(FilterConfig filterConfig) throws ServletException {
@@ -59,6 +66,20 @@ public class RestCsrfPreventionFilter implements Filter {
} else {
parseMethodsToIgnore(METHODS_TO_IGNORE_DEFAULT);
}
+
+ String agents = filterConfig.getInitParameter(BROWSER_USER_AGENT_PARAM);
+ if (agents == null) {
+ agents = BROWSER_USER_AGENTS_DEFAULT;
+ }
+ parseBrowserUserAgents(agents);
+ }
+
+ void parseBrowserUserAgents(String userAgents) {
+ String[] agentsArray = userAgents.split(",");
+ browserUserAgents = new HashSet<Pattern>();
+ for (String patternString : agentsArray) {
+ browserUserAgents.add(Pattern.compile(patternString));
+ }
}
void parseMethodsToIgnore(String mti) {
@@ -69,17 +90,46 @@ public class RestCsrfPreventionFilter implements Filter {
}
}
+ /**
+ * This method interrogates the User-Agent String and returns whether it
+ * refers to a browser. If its not a browser, then the requirement for the
+ * CSRF header will not be enforced; if it is a browser, the requirement will
+ * be enforced.
+ * <p>
+ * A User-Agent String is considered to be a browser if it matches
+ * any of the regex patterns from browser-useragent-regex; the default
+ * behavior is to consider everything a browser that matches the following:
+ * "^Mozilla.*,^Opera.*". Subclasses can optionally override
+ * this method to use different behavior.
+ *
+ * @param userAgent The User-Agent String, or null if there isn't one
+ * @return true if the User-Agent String refers to a browser, false if not
+ */
+ protected boolean isBrowser(String userAgent) {
+ if (userAgent == null) {
+ return false;
+ }
+ for (Pattern pattern : browserUserAgents) {
+ Matcher matcher = pattern.matcher(userAgent);
+ if (matcher.matches()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest)request;
- if (methodsToIgnore.contains(httpRequest.getMethod()) ||
+ if (!isBrowser(httpRequest.getHeader(HEADER_USER_AGENT)) ||
+ methodsToIgnore.contains(httpRequest.getMethod()) ||
httpRequest.getHeader(headerName) != null) {
chain.doFilter(request, response);
} else {
((HttpServletResponse)response).sendError(
HttpServletResponse.SC_BAD_REQUEST,
- "Missing Required Header for Vulnerability Protection");
+ "Missing Required Header for CSRF Vulnerability Protection");
}
}
http://git-wip-us.apache.org/repos/asf/hadoop/blob/a37e423e/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/security/http/TestRestCsrfPreventionFilter.java
----------------------------------------------------------------------
diff --git a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/security/http/TestRestCsrfPreventionFilter.java b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/security/http/TestRestCsrfPreventionFilter.java
index adf89f5..29dccd3 100644
--- a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/security/http/TestRestCsrfPreventionFilter.java
+++ b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/security/http/TestRestCsrfPreventionFilter.java
@@ -34,8 +34,12 @@ import org.mockito.Mockito;
public class TestRestCsrfPreventionFilter {
+ private static final String NON_BROWSER = "java";
+ private static final String BROWSER_AGENT =
+ "Mozilla/5.0 (compatible; U; ABrowse 0.6; Syllable)" +
+ " AppleWebKit/420+ (KHTML, like Gecko)";
private static final String EXPECTED_MESSAGE =
- "Missing Required Header for Vulnerability Protection";
+ "Missing Required Header for CSRF Vulnerability Protection";
private static final String X_CUSTOM_HEADER = "X-CUSTOM_HEADER";
@Test
@@ -52,7 +56,44 @@ public class TestRestCsrfPreventionFilter {
// CSRF has not been sent
HttpServletRequest mockReq = Mockito.mock(HttpServletRequest.class);
Mockito.when(mockReq.getHeader(RestCsrfPreventionFilter.HEADER_DEFAULT)).
+ thenReturn(null);
+ Mockito.when(mockReq.getHeader(RestCsrfPreventionFilter.HEADER_USER_AGENT)).
+ thenReturn(BROWSER_AGENT);
+
+ // Objects to verify interactions based on request
+ HttpServletResponse mockRes = Mockito.mock(HttpServletResponse.class);
+ FilterChain mockChain = Mockito.mock(FilterChain.class);
+
+ // Object under test
+ RestCsrfPreventionFilter filter = new RestCsrfPreventionFilter();
+ filter.init(filterConfig);
+ filter.doFilter(mockReq, mockRes, mockChain);
+
+ verify(mockRes, atLeastOnce()).sendError(
+ HttpServletResponse.SC_BAD_REQUEST, EXPECTED_MESSAGE);
+ Mockito.verifyZeroInteractions(mockChain);
+ }
+
+ @Test
+ public void testNoHeaderCustomAgentConfig_badRequest()
+ throws ServletException, IOException {
+ // Setup the configuration settings of the server
+ FilterConfig filterConfig = Mockito.mock(FilterConfig.class);
+ Mockito.when(filterConfig.getInitParameter(
+ RestCsrfPreventionFilter.CUSTOM_HEADER_PARAM)).thenReturn(null);
+ Mockito.when(filterConfig.getInitParameter(
+ RestCsrfPreventionFilter.CUSTOM_METHODS_TO_IGNORE_PARAM)).
thenReturn(null);
+ Mockito.when(filterConfig.getInitParameter(
+ RestCsrfPreventionFilter.BROWSER_USER_AGENT_PARAM)).
+ thenReturn("^Mozilla.*,^Opera.*,curl");
+
+ // CSRF has not been sent
+ HttpServletRequest mockReq = Mockito.mock(HttpServletRequest.class);
+ Mockito.when(mockReq.getHeader(RestCsrfPreventionFilter.HEADER_DEFAULT)).
+ thenReturn(null);
+ Mockito.when(mockReq.getHeader(RestCsrfPreventionFilter.HEADER_USER_AGENT)).
+ thenReturn("curl");
// Objects to verify interactions based on request
HttpServletResponse mockRes = Mockito.mock(HttpServletResponse.class);
@@ -69,6 +110,36 @@ public class TestRestCsrfPreventionFilter {
}
@Test
+ public void testNoHeaderDefaultConfigNonBrowser_goodRequest()
+ throws ServletException, IOException {
+ // Setup the configuration settings of the server
+ FilterConfig filterConfig = Mockito.mock(FilterConfig.class);
+ Mockito.when(filterConfig.getInitParameter(
+ RestCsrfPreventionFilter.CUSTOM_HEADER_PARAM)).thenReturn(null);
+ Mockito.when(filterConfig.getInitParameter(
+ RestCsrfPreventionFilter.CUSTOM_METHODS_TO_IGNORE_PARAM)).
+ thenReturn(null);
+
+ // CSRF has not been sent
+ HttpServletRequest mockReq = Mockito.mock(HttpServletRequest.class);
+ Mockito.when(mockReq.getHeader(RestCsrfPreventionFilter.HEADER_DEFAULT)).
+ thenReturn(null);
+ Mockito.when(mockReq.getHeader(RestCsrfPreventionFilter.HEADER_USER_AGENT)).
+ thenReturn(NON_BROWSER);
+
+ // Objects to verify interactions based on request
+ HttpServletResponse mockRes = Mockito.mock(HttpServletResponse.class);
+ FilterChain mockChain = Mockito.mock(FilterChain.class);
+
+ // Object under test
+ RestCsrfPreventionFilter filter = new RestCsrfPreventionFilter();
+ filter.init(filterConfig);
+ filter.doFilter(mockReq, mockRes, mockChain);
+
+ Mockito.verify(mockChain).doFilter(mockReq, mockRes);
+ }
+
+ @Test
public void testHeaderPresentDefaultConfig_goodRequest()
throws ServletException, IOException {
// Setup the configuration settings of the server
@@ -136,9 +207,11 @@ public class TestRestCsrfPreventionFilter {
Mockito.when(filterConfig.getInitParameter(
RestCsrfPreventionFilter.CUSTOM_METHODS_TO_IGNORE_PARAM)).
thenReturn(null);
+ HttpServletRequest mockReq = Mockito.mock(HttpServletRequest.class);
+ Mockito.when(mockReq.getHeader(RestCsrfPreventionFilter.HEADER_USER_AGENT)).
+ thenReturn(BROWSER_AGENT);
// CSRF has not been sent
- HttpServletRequest mockReq = Mockito.mock(HttpServletRequest.class);
Mockito.when(mockReq.getHeader(RestCsrfPreventionFilter.HEADER_DEFAULT)).
thenReturn(null);
@@ -164,9 +237,11 @@ public class TestRestCsrfPreventionFilter {
Mockito.when(filterConfig.getInitParameter(
RestCsrfPreventionFilter.CUSTOM_METHODS_TO_IGNORE_PARAM)).
thenReturn("");
+ HttpServletRequest mockReq = Mockito.mock(HttpServletRequest.class);
+ Mockito.when(mockReq.getHeader(RestCsrfPreventionFilter.HEADER_USER_AGENT)).
+ thenReturn(BROWSER_AGENT);
// CSRF has not been sent
- HttpServletRequest mockReq = Mockito.mock(HttpServletRequest.class);
Mockito.when(mockReq.getHeader(RestCsrfPreventionFilter.HEADER_DEFAULT)).
thenReturn(null);
Mockito.when(mockReq.getMethod()).
@@ -194,9 +269,11 @@ public class TestRestCsrfPreventionFilter {
Mockito.when(filterConfig.getInitParameter(
RestCsrfPreventionFilter.CUSTOM_METHODS_TO_IGNORE_PARAM)).
thenReturn("GET");
+ HttpServletRequest mockReq = Mockito.mock(HttpServletRequest.class);
+ Mockito.when(mockReq.getHeader(RestCsrfPreventionFilter.HEADER_USER_AGENT)).
+ thenReturn(BROWSER_AGENT);
// CSRF has not been sent
- HttpServletRequest mockReq = Mockito.mock(HttpServletRequest.class);
Mockito.when(mockReq.getHeader(RestCsrfPreventionFilter.HEADER_DEFAULT)).
thenReturn(null);
Mockito.when(mockReq.getMethod()).
@@ -224,9 +301,11 @@ public class TestRestCsrfPreventionFilter {
Mockito.when(filterConfig.getInitParameter(
RestCsrfPreventionFilter.CUSTOM_METHODS_TO_IGNORE_PARAM)).
thenReturn("GET,OPTIONS");
+ HttpServletRequest mockReq = Mockito.mock(HttpServletRequest.class);
+ Mockito.when(mockReq.getHeader(RestCsrfPreventionFilter.HEADER_USER_AGENT)).
+ thenReturn(BROWSER_AGENT);
// CSRF has not been sent
- HttpServletRequest mockReq = Mockito.mock(HttpServletRequest.class);
Mockito.when(mockReq.getHeader(RestCsrfPreventionFilter.HEADER_DEFAULT)).
thenReturn(null);
Mockito.when(mockReq.getMethod()).
@@ -254,9 +333,11 @@ public class TestRestCsrfPreventionFilter {
Mockito.when(filterConfig.getInitParameter(
RestCsrfPreventionFilter.CUSTOM_METHODS_TO_IGNORE_PARAM)).
thenReturn("GET,OPTIONS");
+ HttpServletRequest mockReq = Mockito.mock(HttpServletRequest.class);
+ Mockito.when(mockReq.getHeader(RestCsrfPreventionFilter.HEADER_USER_AGENT)).
+ thenReturn(BROWSER_AGENT);
// CSRF has not been sent
- HttpServletRequest mockReq = Mockito.mock(HttpServletRequest.class);
Mockito.when(mockReq.getHeader(RestCsrfPreventionFilter.HEADER_DEFAULT)).
thenReturn(null);
Mockito.when(mockReq.getMethod()).