Skip to content

Commit

Permalink
Extract common functionality from CsrfPreventionFilter to CsrfPrevent…
Browse files Browse the repository at this point in the history
…ionFilterBase

git-svn-id: https://svn.apache.org/repos/asf/tomcat/trunk@1708957 13f79535-47bb-0310-9956-ffa450edef68
  • Loading branch information
violetagg committed Oct 16, 2015
1 parent 0440d91 commit b1ea1b3
Show file tree
Hide file tree
Showing 2 changed files with 126 additions and 111 deletions.
113 changes: 2 additions & 111 deletions java/org/apache/catalina/filters/CsrfPreventionFilter.java
Expand Up @@ -18,15 +18,12 @@


import java.io.IOException; import java.io.IOException;
import java.io.Serializable; import java.io.Serializable;
import java.security.SecureRandom;
import java.util.HashSet; import java.util.HashSet;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map;
import java.util.Random;
import java.util.Set; import java.util.Set;


import javax.servlet.FilterChain; import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException; import javax.servlet.ServletException;
import javax.servlet.ServletRequest; import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse; import javax.servlet.ServletResponse;
Expand All @@ -35,9 +32,6 @@
import javax.servlet.http.HttpServletResponseWrapper; import javax.servlet.http.HttpServletResponseWrapper;
import javax.servlet.http.HttpSession; import javax.servlet.http.HttpSession;


import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;

/** /**
* Provides basic CSRF protection for a web application. The filter assumes * Provides basic CSRF protection for a web application. The filter assumes
* that: * that:
Expand All @@ -48,44 +42,12 @@
* returned to the client * returned to the client
* </ul> * </ul>
*/ */
public class CsrfPreventionFilter extends FilterBase { public class CsrfPreventionFilter extends CsrfPreventionFilterBase {

private static final Log log =
LogFactory.getLog(CsrfPreventionFilter.class);

private String randomClass = SecureRandom.class.getName();

private Random randomSource;

private int denyStatus = HttpServletResponse.SC_FORBIDDEN;


private final Set<String> entryPoints = new HashSet<>(); private final Set<String> entryPoints = new HashSet<>();


private int nonceCacheSize = 5; private int nonceCacheSize = 5;


@Override
protected Log getLogger() {
return log;
}

/**
* Return response status code that is used to reject denied request.
*/
public int getDenyStatus() {
return denyStatus;
}

/**
* Set response status code that is used to reject denied request. If none
* set, the default value of 403 will be used.
*
* @param denyStatus
* HTTP status code
*/
public void setDenyStatus(int denyStatus) {
this.denyStatus = denyStatus;
}

/** /**
* Entry points are URLs that will not be tested for the presence of a valid * Entry points are URLs that will not be tested for the presence of a valid
* nonce. They are used to provide a way to navigate back to a protected * nonce. They are used to provide a way to navigate back to a protected
Expand Down Expand Up @@ -116,39 +78,6 @@ public void setNonceCacheSize(int nonceCacheSize) {
this.nonceCacheSize = nonceCacheSize; this.nonceCacheSize = nonceCacheSize;
} }


/**
* Specify the class to use to generate the nonces. Must be in instance of
* {@link Random}.
*
* @param randomClass The name of the class to use
*/
public void setRandomClass(String randomClass) {
this.randomClass = randomClass;
}

@Override
public void init(FilterConfig filterConfig) throws ServletException {
// Set the parameters
super.init(filterConfig);

try {
Class<?> clazz = Class.forName(randomClass);
randomSource = (Random) clazz.newInstance();
} catch (ClassNotFoundException e) {
ServletException se = new ServletException(sm.getString(
"csrfPrevention.invalidRandomClass", randomClass), e);
throw se;
} catch (InstantiationException e) {
ServletException se = new ServletException(sm.getString(
"csrfPrevention.invalidRandomClass", randomClass), e);
throw se;
} catch (IllegalAccessException e) {
ServletException se = new ServletException(sm.getString(
"csrfPrevention.invalidRandomClass", randomClass), e);
throw se;
}
}

@Override @Override
public void doFilter(ServletRequest request, ServletResponse response, public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException { FilterChain chain) throws IOException, ServletException {
Expand Down Expand Up @@ -187,7 +116,7 @@ public void doFilter(ServletRequest request, ServletResponse response,


if (nonceCache == null || previousNonce == null || if (nonceCache == null || previousNonce == null ||
!nonceCache.contains(previousNonce)) { !nonceCache.contains(previousNonce)) {
res.sendError(denyStatus); res.sendError(getDenyStatus());
return; return;
} }
} }
Expand All @@ -214,44 +143,6 @@ public void doFilter(ServletRequest request, ServletResponse response,
} }




@Override
protected boolean isConfigProblemFatal() {
return true;
}


/**
* Generate a once time token (nonce) for authenticating subsequent
* requests. This will also add the token to the session. The nonce
* generation is a simplified version of ManagerBase.generateSessionId().
*
*/
protected String generateNonce() {
byte random[] = new byte[16];

// Render the result as a String of hexadecimal digits
StringBuilder buffer = new StringBuilder();

randomSource.nextBytes(random);

for (int j = 0; j < random.length; j++) {
byte b1 = (byte) ((random[j] & 0xf0) >> 4);
byte b2 = (byte) (random[j] & 0x0f);
if (b1 < 10) {
buffer.append((char) ('0' + b1));
} else {
buffer.append((char) ('A' + (b1 - 10)));
}
if (b2 < 10) {
buffer.append((char) ('0' + b2));
} else {
buffer.append((char) ('A' + (b2 - 10)));
}
}

return buffer.toString();
}

protected static class CsrfResponseWrapper protected static class CsrfResponseWrapper
extends HttpServletResponseWrapper { extends HttpServletResponseWrapper {


Expand Down
124 changes: 124 additions & 0 deletions java/org/apache/catalina/filters/CsrfPreventionFilterBase.java
@@ -0,0 +1,124 @@
/*
* 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.catalina.filters;

import java.security.SecureRandom;
import java.util.Random;

import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletResponse;

import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;

public abstract class CsrfPreventionFilterBase extends FilterBase {

private static final Log log = LogFactory.getLog(CsrfPreventionFilterBase.class);

private String randomClass = SecureRandom.class.getName();

private Random randomSource;

private int denyStatus = HttpServletResponse.SC_FORBIDDEN;

@Override
protected Log getLogger() {
return log;
}

/**
* Return response status code that is used to reject denied request.
*/
public int getDenyStatus() {
return denyStatus;
}

/**
* Set response status code that is used to reject denied request. If none
* set, the default value of 403 will be used.
*
* @param denyStatus
* HTTP status code
*/
public void setDenyStatus(int denyStatus) {
this.denyStatus = denyStatus;
}

/**
* Specify the class to use to generate the nonces. Must be in instance of
* {@link Random}.
*
* @param randomClass
* The name of the class to use
*/
public void setRandomClass(String randomClass) {
this.randomClass = randomClass;
}

@Override
public void init(FilterConfig filterConfig) throws ServletException {
// Set the parameters
super.init(filterConfig);

try {
Class<?> clazz = Class.forName(randomClass);
randomSource = (Random) clazz.newInstance();
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
ServletException se = new ServletException(sm.getString(
"csrfPrevention.invalidRandomClass", randomClass), e);
throw se;
}
}

@Override
protected boolean isConfigProblemFatal() {
return true;
}

/**
* Generate a once time token (nonce) for authenticating subsequent
* requests. The nonce generation is a simplified version of
* ManagerBase.generateSessionId().
*/
protected String generateNonce() {
byte random[] = new byte[16];

// Render the result as a String of hexadecimal digits
StringBuilder buffer = new StringBuilder();

randomSource.nextBytes(random);

for (int j = 0; j < random.length; j++) {
byte b1 = (byte) ((random[j] & 0xf0) >> 4);
byte b2 = (byte) (random[j] & 0x0f);
if (b1 < 10) {
buffer.append((char) ('0' + b1));
} else {
buffer.append((char) ('A' + (b1 - 10)));
}
if (b2 < 10) {
buffer.append((char) ('0' + b2));
} else {
buffer.append((char) ('A' + (b2 - 10)));
}
}

return buffer.toString();
}

}

0 comments on commit b1ea1b3

Please sign in to comment.