You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by ma...@apache.org on 2022/05/09 17:36:59 UTC
[tomcat] branch 9.0.x updated: Fix BZ 65853 - refactor CsrfPreventionFilter to make extension easier
This is an automated email from the ASF dual-hosted git repository.
markt pushed a commit to branch 9.0.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git
The following commit(s) were added to refs/heads/9.0.x by this push:
new e0e9e19024 Fix BZ 65853 - refactor CsrfPreventionFilter to make extension easier
e0e9e19024 is described below
commit e0e9e19024cdd56422652e7c6ecf1d555428664e
Author: Mark Thomas <ma...@apache.org>
AuthorDate: Mon May 9 18:34:52 2022 +0100
Fix BZ 65853 - refactor CsrfPreventionFilter to make extension easier
---
.../catalina/filters/CsrfPreventionFilter.java | 93 +++++++++++++++++-----
.../catalina/filters/CsrfPreventionFilterBase.java | 19 +++++
.../catalina/filters/RestCsrfPreventionFilter.java | 2 +-
webapps/docs/changelog.xml | 5 ++
4 files changed, 97 insertions(+), 22 deletions(-)
diff --git a/java/org/apache/catalina/filters/CsrfPreventionFilter.java b/java/org/apache/catalina/filters/CsrfPreventionFilter.java
index fe7a27f92d..6e45f3d111 100644
--- a/java/org/apache/catalina/filters/CsrfPreventionFilter.java
+++ b/java/org/apache/catalina/filters/CsrfPreventionFilter.java
@@ -118,25 +118,11 @@ public class CsrfPreventionFilter extends CsrfPreventionFilterBase {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse res = (HttpServletResponse) response;
- boolean skipNonceCheck = false;
-
- if (Constants.METHOD_GET.equals(req.getMethod())
- && entryPoints.contains(getRequestedPath(req))) {
- if(log.isTraceEnabled()) {
- log.trace("Skipping CSRF nonce-check for GET request to entry point " + getRequestedPath(req));
- }
-
- skipNonceCheck = true;
- }
-
HttpSession session = req.getSession(false);
- @SuppressWarnings("unchecked")
- LruCache<String> nonceCache = (session == null) ? null
- : (LruCache<String>) session.getAttribute(
- Constants.CSRF_NONCE_SESSION_ATTR_NAME);
+ NonceCache<String> nonceCache = (session == null) ? null : getNonceCache(req, session);
- if (!skipNonceCheck) {
+ if (!skipNonceCheck(req)) {
String previousNonce =
req.getParameter(nonceRequestParameterName);
@@ -182,7 +168,6 @@ public class CsrfPreventionFilter extends CsrfPreventionFilterBase {
log.debug("Creating new CSRF nonce cache with size=" + nonceCacheSize + " for session " + (null == session ? "(will create)" : session.getId()));
}
- nonceCache = new LruCache<>(nonceCacheSize);
if (session == null) {
if(log.isDebugEnabled()) {
log.debug("Creating new session to store CSRF nonce cache");
@@ -190,11 +175,11 @@ public class CsrfPreventionFilter extends CsrfPreventionFilterBase {
session = req.getSession(true);
}
- session.setAttribute(
- Constants.CSRF_NONCE_SESSION_ATTR_NAME, nonceCache);
+
+ nonceCache = createNonceCache(req, session);
}
- String newNonce = generateNonce();
+ String newNonce = generateNonce(req);
nonceCache.add(newNonce);
@@ -212,6 +197,64 @@ public class CsrfPreventionFilter extends CsrfPreventionFilterBase {
}
+ protected boolean skipNonceCheck(HttpServletRequest request) {
+ if (!Constants.METHOD_GET.equals(request.getMethod())) {
+ return false;
+ }
+
+ String requestedPath = getRequestedPath(request);
+
+ if (!entryPoints.contains(requestedPath)) {
+ return false;
+ }
+
+ if (log.isTraceEnabled()) {
+ log.trace("Skipping CSRF nonce-check for GET request to entry point " + requestedPath);
+ }
+
+ return true;
+ }
+
+
+ /**
+ * Create a new {@link NonceCache} and store in the {@link HttpSession}.
+ * This method is provided primarily for the benefit of sub-classes that
+ * wish to customise this behaviour.
+ *
+ * @param request The request that triggered the need to create the nonce
+ * cache. Unused by the default implementation.
+ * @param session The session associated with the request.
+ *
+ * @return A newly created {@link NonceCache}
+ */
+ protected NonceCache<String> createNonceCache(HttpServletRequest request, HttpSession session) {
+
+ NonceCache<String> nonceCache = new LruCache<>(nonceCacheSize);
+
+ session.setAttribute(Constants.CSRF_NONCE_SESSION_ATTR_NAME, nonceCache);
+
+ return nonceCache;
+ }
+
+
+ /**
+ * Obtain the {@link NonceCache} associated with the request and/or session.
+ * This method is provided primarily for the benefit of sub-classes that
+ * wish to customise this behaviour.
+ *
+ * @param request The request that triggered the need to obtain the nonce
+ * cache. Unused by the default implementation.
+ * @param session The session associated with the request.
+ *
+ * @return A newly created {@link NonceCache}
+ */
+ protected NonceCache<String> getNonceCache(HttpServletRequest request, HttpSession session) {
+ @SuppressWarnings("unchecked")
+ NonceCache<String> nonceCache =
+ (NonceCache<String>) session.getAttribute(Constants.CSRF_NONCE_SESSION_ATTR_NAME);
+ return nonceCache;
+ }
+
protected static class CsrfResponseWrapper
extends HttpServletResponseWrapper {
@@ -285,7 +328,15 @@ public class CsrfPreventionFilter extends CsrfPreventionFilterBase {
}
}
- protected static class LruCache<T> implements Serializable {
+
+ protected static interface NonceCache<T> extends Serializable {
+ void add(T nonce);
+
+ boolean contains(T nonce);
+ }
+
+
+ protected static class LruCache<T> implements NonceCache<T> {
private static final long serialVersionUID = 1L;
diff --git a/java/org/apache/catalina/filters/CsrfPreventionFilterBase.java b/java/org/apache/catalina/filters/CsrfPreventionFilterBase.java
index 1f51fa6b58..ba35ec42a5 100644
--- a/java/org/apache/catalina/filters/CsrfPreventionFilterBase.java
+++ b/java/org/apache/catalina/filters/CsrfPreventionFilterBase.java
@@ -93,13 +93,32 @@ public abstract class CsrfPreventionFilterBase extends FilterBase {
return true;
}
+
+ /**
+ * Generate a once time token (nonce) for authenticating subsequent
+ * requests. The nonce generation is a simplified version of
+ * ManagerBase.generateSessionId().
+ *
+ * @param request The request. Unused in this method but present for the
+ * the benefit of sub-classes.
+ *
+ * @return the generated nonce
+ */
+ protected String generateNonce(HttpServletRequest request) {
+ return generateNonce();
+ }
+
/**
* Generate a once time token (nonce) for authenticating subsequent
* requests. The nonce generation is a simplified version of
* ManagerBase.generateSessionId().
*
* @return the generated nonce
+ *
+ * @deprecated Use {@link #generateNonce(HttpServletRequest)} instead. This
+ * method will be removed in Apache Tomcat 10.1.x onwards.
*/
+ @Deprecated
protected String generateNonce() {
byte random[] = new byte[16];
diff --git a/java/org/apache/catalina/filters/RestCsrfPreventionFilter.java b/java/org/apache/catalina/filters/RestCsrfPreventionFilter.java
index b7811bb9f9..58f76f1467 100644
--- a/java/org/apache/catalina/filters/RestCsrfPreventionFilter.java
+++ b/java/org/apache/catalina/filters/RestCsrfPreventionFilter.java
@@ -210,7 +210,7 @@ public class RestCsrfPreventionFilter extends CsrfPreventionFilterBase {
String nonceFromSessionStr = nonceFromSession.getNonce(request.getSession(false),
Constants.CSRF_REST_NONCE_SESSION_ATTR_NAME);
if (nonceFromSessionStr == null) {
- nonceFromSessionStr = generateNonce();
+ nonceFromSessionStr = generateNonce(request);
nonceToSession.setNonce(Objects.requireNonNull(request.getSession(true)),
Constants.CSRF_REST_NONCE_SESSION_ATTR_NAME, nonceFromSessionStr);
}
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index f605735901..1e3ca3bdf3 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -107,6 +107,11 @@
<section name="Tomcat 9.0.63 (remm)" rtext="in development">
<subsection name="Catalina">
<changelog>
+ <scode>
+ <bug>65853</bug>: Refactor the <code>CsrfPreventionFilter</code> to make
+ it easier for sub-classes to modify the nonce generation and storage.
+ Based on suggestions by Marvin Fröhlich. (markt)
+ </scode>
<fix>
<bug>65991</bug>: Avoid NPE with <code>SSLAuthenticator</code> when
<code>boundOnInit</code> is used on a connector, during the check
---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org