CVE-2016-0714
Description
The session-persistence implementation in Apache Tomcat 6.x before 6.0.45, 7.x before 7.0.68, 8.x before 8.0.31, and 9.x before 9.0.0.M2 mishandles session attributes, which allows remote authenticated users to bypass intended SecurityManager restrictions and execute arbitrary code in a privileged context via a web application that places a crafted object in a session.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
org.apache.tomcat:tomcatMaven | >= 9.0.0.M1, < 9.0.0.M2 | 9.0.0.M2 |
org.apache.tomcat:tomcatMaven | >= 8.0.0.RC1, < 8.0.32 | 8.0.32 |
org.apache.tomcat:tomcatMaven | >= 7.0.0, < 7.0.70 | 7.0.70 |
org.apache.tomcat:tomcatMaven | >= 6.0.0, < 6.0.46 | 6.0.46 |
Affected products
104cpe:2.3:a:apache:tomcat:6.0.0:*:*:*:*:*:*:*+ 97 more
- cpe:2.3:a:apache:tomcat:6.0.0:*:*:*:*:*:*:*
- cpe:2.3:a:apache:tomcat:6.0.0:alpha:*:*:*:*:*:*
- cpe:2.3:a:apache:tomcat:6.0.1:*:*:*:*:*:*:*
- cpe:2.3:a:apache:tomcat:6.0.10:*:*:*:*:*:*:*
- cpe:2.3:a:apache:tomcat:6.0.11:*:*:*:*:*:*:*
- cpe:2.3:a:apache:tomcat:6.0.13:*:*:*:*:*:*:*
- cpe:2.3:a:apache:tomcat:6.0.14:*:*:*:*:*:*:*
- cpe:2.3:a:apache:tomcat:6.0.16:*:*:*:*:*:*:*
- cpe:2.3:a:apache:tomcat:6.0.18:*:*:*:*:*:*:*
- cpe:2.3:a:apache:tomcat:6.0.1:alpha:*:*:*:*:*:*
- cpe:2.3:a:apache:tomcat:6.0.2:*:*:*:*:*:*:*
- cpe:2.3:a:apache:tomcat:6.0.20:*:*:*:*:*:*:*
- cpe:2.3:a:apache:tomcat:6.0.24:*:*:*:*:*:*:*
- cpe:2.3:a:apache:tomcat:6.0.26:*:*:*:*:*:*:*
- cpe:2.3:a:apache:tomcat:6.0.28:*:*:*:*:*:*:*
- cpe:2.3:a:apache:tomcat:6.0.29:*:*:*:*:*:*:*
- cpe:2.3:a:apache:tomcat:6.0.2:alpha:*:*:*:*:*:*
- cpe:2.3:a:apache:tomcat:6.0.2:beta:*:*:*:*:*:*
- cpe:2.3:a:apache:tomcat:6.0.30:*:*:*:*:*:*:*
- cpe:2.3:a:apache:tomcat:6.0.32:*:*:*:*:*:*:*
- cpe:2.3:a:apache:tomcat:6.0.33:*:*:*:*:*:*:*
- cpe:2.3:a:apache:tomcat:6.0.35:*:*:*:*:*:*:*
- cpe:2.3:a:apache:tomcat:6.0.36:*:*:*:*:*:*:*
- cpe:2.3:a:apache:tomcat:6.0.37:*:*:*:*:*:*:*
- cpe:2.3:a:apache:tomcat:6.0.39:*:*:*:*:*:*:*
- cpe:2.3:a:apache:tomcat:6.0.4:*:*:*:*:*:*:*
- cpe:2.3:a:apache:tomcat:6.0.41:*:*:*:*:*:*:*
- cpe:2.3:a:apache:tomcat:6.0.43:*:*:*:*:*:*:*
- cpe:2.3:a:apache:tomcat:6.0.44:*:*:*:*:*:*:*
- cpe:2.3:a:apache:tomcat:6.0.4:alpha:*:*:*:*:*:*
- cpe:2.3:a:apache:tomcat:7.0.0:beta:*:*:*:*:*:*
- cpe:2.3:a:apache:tomcat:7.0.10:*:*:*:*:*:*:*
- cpe:2.3:a:apache:tomcat:7.0.11:*:*:*:*:*:*:*
- cpe:2.3:a:apache:tomcat:7.0.12:*:*:*:*:*:*:*
- cpe:2.3:a:apache:tomcat:7.0.14:*:*:*:*:*:*:*
- cpe:2.3:a:apache:tomcat:7.0.16:*:*:*:*:*:*:*
- cpe:2.3:a:apache:tomcat:7.0.19:*:*:*:*:*:*:*
- cpe:2.3:a:apache:tomcat:7.0.20:*:*:*:*:*:*:*
- cpe:2.3:a:apache:tomcat:7.0.21:*:*:*:*:*:*:*
- cpe:2.3:a:apache:tomcat:7.0.22:*:*:*:*:*:*:*
- cpe:2.3:a:apache:tomcat:7.0.23:*:*:*:*:*:*:*
- cpe:2.3:a:apache:tomcat:7.0.25:*:*:*:*:*:*:*
- cpe:2.3:a:apache:tomcat:7.0.26:*:*:*:*:*:*:*
- cpe:2.3:a:apache:tomcat:7.0.27:*:*:*:*:*:*:*
- cpe:2.3:a:apache:tomcat:7.0.28:*:*:*:*:*:*:*
- cpe:2.3:a:apache:tomcat:7.0.29:*:*:*:*:*:*:*
- cpe:2.3:a:apache:tomcat:7.0.2:beta:*:*:*:*:*:*
- cpe:2.3:a:apache:tomcat:7.0.30:*:*:*:*:*:*:*
- cpe:2.3:a:apache:tomcat:7.0.32:*:*:*:*:*:*:*
- cpe:2.3:a:apache:tomcat:7.0.33:*:*:*:*:*:*:*
- cpe:2.3:a:apache:tomcat:7.0.34:*:*:*:*:*:*:*
- cpe:2.3:a:apache:tomcat:7.0.35:*:*:*:*:*:*:*
- cpe:2.3:a:apache:tomcat:7.0.37:*:*:*:*:*:*:*
- cpe:2.3:a:apache:tomcat:7.0.39:*:*:*:*:*:*:*
- cpe:2.3:a:apache:tomcat:7.0.40:*:*:*:*:*:*:*
- cpe:2.3:a:apache:tomcat:7.0.41:*:*:*:*:*:*:*
- cpe:2.3:a:apache:tomcat:7.0.42:*:*:*:*:*:*:*
- cpe:2.3:a:apache:tomcat:7.0.47:*:*:*:*:*:*:*
- cpe:2.3:a:apache:tomcat:7.0.4:beta:*:*:*:*:*:*
- cpe:2.3:a:apache:tomcat:7.0.50:*:*:*:*:*:*:*
- cpe:2.3:a:apache:tomcat:7.0.52:*:*:*:*:*:*:*
- cpe:2.3:a:apache:tomcat:7.0.53:*:*:*:*:*:*:*
- cpe:2.3:a:apache:tomcat:7.0.54:*:*:*:*:*:*:*
- cpe:2.3:a:apache:tomcat:7.0.55:*:*:*:*:*:*:*
- cpe:2.3:a:apache:tomcat:7.0.56:*:*:*:*:*:*:*
- cpe:2.3:a:apache:tomcat:7.0.57:*:*:*:*:*:*:*
- cpe:2.3:a:apache:tomcat:7.0.59:*:*:*:*:*:*:*
- cpe:2.3:a:apache:tomcat:7.0.5:beta:*:*:*:*:*:*
- cpe:2.3:a:apache:tomcat:7.0.6:*:*:*:*:*:*:*
- cpe:2.3:a:apache:tomcat:7.0.61:*:*:*:*:*:*:*
- cpe:2.3:a:apache:tomcat:7.0.62:*:*:*:*:*:*:*
- cpe:2.3:a:apache:tomcat:7.0.63:*:*:*:*:*:*:*
- cpe:2.3:a:apache:tomcat:7.0.64:*:*:*:*:*:*:*
- cpe:2.3:a:apache:tomcat:7.0.65:*:*:*:*:*:*:*
- cpe:2.3:a:apache:tomcat:7.0.67:*:*:*:*:*:*:*
- cpe:2.3:a:apache:tomcat:8.0.0:rc1:*:*:*:*:*:*
- cpe:2.3:a:apache:tomcat:8.0.0:rc10:*:*:*:*:*:*
- cpe:2.3:a:apache:tomcat:8.0.0:rc3:*:*:*:*:*:*
- cpe:2.3:a:apache:tomcat:8.0.0:rc5:*:*:*:*:*:*
- cpe:2.3:a:apache:tomcat:8.0.1:*:*:*:*:*:*:*
- cpe:2.3:a:apache:tomcat:8.0.11:*:*:*:*:*:*:*
- cpe:2.3:a:apache:tomcat:8.0.12:*:*:*:*:*:*:*
- cpe:2.3:a:apache:tomcat:8.0.14:*:*:*:*:*:*:*
- cpe:2.3:a:apache:tomcat:8.0.15:*:*:*:*:*:*:*
- cpe:2.3:a:apache:tomcat:8.0.17:*:*:*:*:*:*:*
- cpe:2.3:a:apache:tomcat:8.0.18:*:*:*:*:*:*:*
- cpe:2.3:a:apache:tomcat:8.0.20:*:*:*:*:*:*:*
- cpe:2.3:a:apache:tomcat:8.0.21:*:*:*:*:*:*:*
- cpe:2.3:a:apache:tomcat:8.0.22:*:*:*:*:*:*:*
- cpe:2.3:a:apache:tomcat:8.0.23:*:*:*:*:*:*:*
- cpe:2.3:a:apache:tomcat:8.0.24:*:*:*:*:*:*:*
- cpe:2.3:a:apache:tomcat:8.0.26:*:*:*:*:*:*:*
- cpe:2.3:a:apache:tomcat:8.0.27:*:*:*:*:*:*:*
- cpe:2.3:a:apache:tomcat:8.0.28:*:*:*:*:*:*:*
- cpe:2.3:a:apache:tomcat:8.0.29:*:*:*:*:*:*:*
- cpe:2.3:a:apache:tomcat:8.0.3:*:*:*:*:*:*:*
- cpe:2.3:a:apache:tomcat:8.0.30:*:*:*:*:*:*:*
- cpe:2.3:a:apache:tomcat:9.0.0:milestone1:*:*:*:*:*:*
cpe:2.3:o:canonical:ubuntu_linux:12.04:*:*:*:lts:*:*:*+ 3 more
- cpe:2.3:o:canonical:ubuntu_linux:12.04:*:*:*:lts:*:*:*
- cpe:2.3:o:canonical:ubuntu_linux:14.04:*:*:*:lts:*:*:*
- cpe:2.3:o:canonical:ubuntu_linux:15.10:*:*:*:*:*:*:*
- cpe:2.3:o:canonical:ubuntu_linux:16.04:*:*:*:lts:*:*:*
cpe:2.3:o:debian:debian_linux:7.0:*:*:*:*:*:*:*+ 1 more
- cpe:2.3:o:debian:debian_linux:7.0:*:*:*:*:*:*:*
- cpe:2.3:o:debian:debian_linux:8.0:*:*:*:*:*:*:*
Patches
8ff1b659dc366When using the new sessionAttributeValueClassNameFilter, apply the filter earlier rather than loading the class and then deciding to filter it out.
8 files changed · +153 −14
java/org/apache/catalina/session/ManagerBase.java+25 −1 modified@@ -39,6 +39,7 @@ import org.apache.catalina.Container; import org.apache.catalina.Context; import org.apache.catalina.Engine; +import org.apache.catalina.Globals; import org.apache.catalina.Lifecycle; import org.apache.catalina.LifecycleException; import org.apache.catalina.Manager; @@ -234,8 +235,31 @@ public abstract class ManagerBase extends LifecycleMBeanBase implements Manager private boolean warnOnSessionAttributeFilterFailure; - // ------------------------------------------------------------- Properties + // ------------------------------------------------------------ Constructors + public ManagerBase() { + if (Globals.IS_SECURITY_ENABLED) { + // Minimum set required for default distribution/persistence to work + // plus String + setSessionAttributeValueClassNameFilter( + "java\\.lang\\.(?:Boolean|Integer|Long|Number|String)"); + setWarnOnSessionAttributeFilterFailure(true); + } + } + + + // -------------------------------------------------------------- Properties + + /** + * Obtain the regular expression used to filter session attribute based on + * attribute name. The regular expression is anchored so it must match the + * entire name + * + * @return The regular expression currently used to filter attribute names. + * {@code null} means no filter is applied. If an empty string is + * specified then no names will match the filter and all attributes + * will be blocked. + */ public String getSessionAttributeNameFilter() { if (sessionAttributeNamePattern == null) { return null;
java/org/apache/catalina/session/StandardManager.java+5 −1 modified@@ -208,17 +208,21 @@ protected void doLoad() throws ClassNotFoundException, IOException { ObjectInputStream ois = null; Loader loader = null; ClassLoader classLoader = null; + Log logger = null; try { fis = new FileInputStream(file.getAbsolutePath()); bis = new BufferedInputStream(fis); loader = container.getLoader(); + logger = container.getLogger(); if (loader != null) { classLoader = loader.getClassLoader(); } if (classLoader == null) { classLoader = getClass().getClassLoader(); } - ois = new CustomObjectInputStream(bis, classLoader); + ois = new CustomObjectInputStream(bis, classLoader, logger, + getSessionAttributeValueClassNamePattern(), + getWarnOnSessionAttributeFilterFailure()); } catch (FileNotFoundException e) { if (log.isDebugEnabled()) { log.debug("No persisted data file found");
java/org/apache/catalina/session/StoreBase.java+14 −1 modified@@ -228,7 +228,20 @@ public void processExpires() { */ protected ObjectInputStream getObjectInputStream(InputStream is) throws IOException { BufferedInputStream bis = new BufferedInputStream(is); - return new CustomObjectInputStream(bis, Thread.currentThread().getContextClassLoader()); + + CustomObjectInputStream ois; + ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); + + if (manager instanceof ManagerBase) { + ManagerBase managerBase = (ManagerBase) manager; + ois = new CustomObjectInputStream(bis, classLoader, manager.getContainer().getLogger(), + managerBase.getSessionAttributeValueClassNamePattern(), + managerBase.getWarnOnSessionAttributeFilterFailure()); + } else { + ois = new CustomObjectInputStream(bis, classLoader); + } + + return ois; }
java/org/apache/catalina/util/CustomObjectInputStream.java+85 −2 modified@@ -18,9 +18,18 @@ import java.io.IOException; import java.io.InputStream; +import java.io.InvalidClassException; import java.io.ObjectInputStream; import java.io.ObjectStreamClass; import java.lang.reflect.Proxy; +import java.util.Collections; +import java.util.Set; +import java.util.WeakHashMap; +import java.util.concurrent.ConcurrentHashMap; +import java.util.regex.Pattern; + +import org.apache.juli.logging.Log; +import org.apache.tomcat.util.res.StringManager; /** * Custom subclass of <code>ObjectInputStream</code> that loads from the @@ -32,23 +41,81 @@ */ public final class CustomObjectInputStream extends ObjectInputStream { + private static final StringManager sm = StringManager.getManager(CustomObjectInputStream.class); + + private static final WeakHashMap<ClassLoader, Set<String>> reportedClassCache = + new WeakHashMap<ClassLoader, Set<String>>(); + /** * The class loader we will use to resolve classes. */ private ClassLoader classLoader = null; + private final Set<String> reportedClasses; + private final Log log; + + private final Pattern allowedClassNamePattern; + private final String allowedClassNameFilter; + private final boolean warnOnFailure; /** - * Construct a new instance of CustomObjectInputStream + * Construct a new instance of CustomObjectInputStream without any filtering + * of deserialized classes. * * @param stream The input stream we will read from * @param classLoader The class loader used to instantiate objects * * @exception IOException if an input/output error occurs */ public CustomObjectInputStream(InputStream stream, ClassLoader classLoader) throws IOException { + this(stream, classLoader, null, null, false); + } + + + /** + * Construct a new instance of CustomObjectInputStream with filtering of + * deserialized classes. + * + * @param stream The input stream we will read from + * @param classLoader The class loader used to instantiate objects + * @param log The logger to use to report any issues. It may only be null if + * the filterMode does not require logging + * @param allowedClassNamePattern The regular expression to use to filter + * deserialized classes. The fully qualified + * class name must match this pattern for + * deserialization to be allowed if filtering + * is enabled. + * @param warnOnFailure Should any failures be logged? + * + * @exception IOException if an input/output error occurs + */ + public CustomObjectInputStream(InputStream stream, ClassLoader classLoader, + Log log, Pattern allowedClassNamePattern, boolean warnOnFailure) + throws IOException { super(stream); + if (log == null && allowedClassNamePattern != null && warnOnFailure) { + throw new IllegalArgumentException( + sm.getString("customObjectInputStream.logRequired")); + } this.classLoader = classLoader; + this.log = log; + this.allowedClassNamePattern = allowedClassNamePattern; + if (allowedClassNamePattern == null) { + this.allowedClassNameFilter = null; + } else { + this.allowedClassNameFilter = allowedClassNamePattern.toString(); + } + this.warnOnFailure = warnOnFailure; + + Set<String> reportedClasses; + synchronized (reportedClassCache) { + reportedClasses = reportedClassCache.get(classLoader); + if (reportedClasses == null) { + reportedClasses = Collections.newSetFromMap(new ConcurrentHashMap<String,Boolean>()); + reportedClassCache.put(classLoader, reportedClasses); + } + } + this.reportedClasses = reportedClasses; } @@ -64,8 +131,24 @@ public CustomObjectInputStream(InputStream stream, ClassLoader classLoader) thro @Override public Class<?> resolveClass(ObjectStreamClass classDesc) throws ClassNotFoundException, IOException { + + String name = classDesc.getName(); + if (allowedClassNamePattern != null) { + boolean allowed = allowedClassNamePattern.matcher(name).matches(); + if (!allowed) { + boolean doLog = warnOnFailure && reportedClasses.add(name); + String msg = sm.getString("customObjectInputStream.nomatch", name, allowedClassNameFilter); + if (doLog) { + log.warn(msg); + } else if (log.isDebugEnabled()) { + log.debug(msg); + } + throw new InvalidClassException(msg); + } + } + try { - return Class.forName(classDesc.getName(), false, classLoader); + return Class.forName(name, false, classLoader); } catch (ClassNotFoundException e) { try { // Try also the superclass because of primitive types
java/org/apache/catalina/util/LocalStrings.properties+2 −0 modified@@ -17,6 +17,8 @@ parameterMap.locked=No modifications are allowed to a locked ParameterMap resourceSet.locked=No modifications are allowed to a locked ResourceSet hexUtil.bad=Bad hexadecimal digit hexUtil.odd=Odd number of hexadecimal digits +customObjectInputStream.logRequired=A valid logger is required for class name filtering with logging +customObjectInputStream.nomatch=The class [{0}] did not match the regular expression [{1}] for classes allowed to be deserialized #Default Messages Utilized by the ExtensionValidator extensionValidator.web-application-manifest=Web Application Manifest extensionValidator.extension-not-found-error=ExtensionValidator[{0}][{1}]: Required extension [{2}] not found.
webapps/docs/changelog.xml+2 −1 modified@@ -177,7 +177,8 @@ based on the implementation class of the value and optional <code>WARN</code> level logging if an attribute is filtered. These options are avaialble for all of the Manager implementations that ship - with Tomcat. (markt) + with Tomcat. When a <code>SecurityManager</code> is used filtering will + be enabled by default. (markt) </add> </changelog> </subsection>
webapps/docs/config/cluster-manager.xml+10 −4 modified@@ -203,7 +203,9 @@ length or <code>null</code>, all attributes are eligible for replication. The pattern is anchored so the fully qualified class name must fully match the pattern. If not specified, the default value of - <code>null</code> will be used.</p> + <code>null</code> will be used unless a <code>SecurityManager</code> is + enabled in which case the default will be + <code>java\\.lang\\.(?:Boolean|Integer|Long|Number|String)</code>.</p> </attribute> <attribute name="stateTimestampDrop" required="false"> When this node sends a <code>GET_ALL_SESSIONS</code> message to other @@ -222,7 +224,8 @@ attribute, should this be logged at <code>WARN</code> level? If <code>WARN</code> level logging is disabled then it will be logged at <code>DEBUG</code>. The default value of this attribute is - <code>false</code>.</p> + <code>false</code> unless a <code>SecurityManager</code> is enabled in + which case the default will be <code>true</code>.</p> </attribute> </attributes> </subsection> @@ -265,7 +268,9 @@ length or <code>null</code>, all attributes are eligible for replication. The pattern is anchored so the fully qualified class name must fully match the pattern. If not specified, the default value of - <code>null</code> will be used.</p> + <code>null</code> will be used unless a <code>SecurityManager</code> is + enabled in which case the default will be + <code>java\\.lang\\.(?:Boolean|Integer|Long|Number|String)</code>.</p> </attribute> <attribute name="terminateOnStartFailure" required="false"> Set to true if you wish to terminate replication map when replication @@ -280,7 +285,8 @@ attribute, should this be logged at <code>WARN</code> level? If <code>WARN</code> level logging is disabled then it will be logged at <code>DEBUG</code>. The default value of this attribute is - <code>false</code>.</p> + <code>false</code> unless a <code>SecurityManager</code> is enabled in + which case the default will be <code>true</code>.</p> </attribute> </attributes> </subsection>
webapps/docs/config/manager.xml+10 −4 modified@@ -198,7 +198,9 @@ length or <code>null</code>, all attributes are eligible for distribution. The pattern is anchored so the fully qualified class name must fully match the pattern. If not specified, the default value of - <code>null</code> will be used.</p> + <code>null</code> will be used unless a <code>SecurityManager</code> is + enabled in which case the default will be + <code>java\\.lang\\.(?:Boolean|Integer|Long|Number|String)</code>.</p> </attribute> <attribute name="warnOnSessionAttributeFilterFailure" required="false"> @@ -207,7 +209,8 @@ attribute, should this be logged at <code>WARN</code> level? If <code>WARN</code> level logging is disabled then it will be logged at <code>DEBUG</code>. The default value of this attribute is - <code>false</code>.</p> + <code>false</code> unless a <code>SecurityManager</code> is enabled in + which case the default will be <code>true</code>.</p> </attribute> </attributes> @@ -318,7 +321,9 @@ length or <code>null</code>, all attributes are eligible for distribution. The pattern is anchored so the fully qualified class name must fully match the pattern. If not specified, the default value of - <code>null</code> will be used.</p> + <code>null</code> will be used unless a <code>SecurityManager</code> is + enabled in which case the default will be + <code>java\\.lang\\.(?:Boolean|Integer|Long|Number|String)</code>.</p> </attribute> <attribute name="warnOnSessionAttributeFilterFailure" required="false"> @@ -327,7 +332,8 @@ attribute, should this be logged at <code>WARN</code> level? If <code>WARN</code> level logging is disabled then it will be logged at <code>DEBUG</code>. The default value of this attribute is - <code>false</code>.</p> + <code>false</code> unless a <code>SecurityManager</code> is enabled in + which case the default will be <code>true</code>.</p> </attribute> </attributes>
79e8ad03404cExpand the session attribute filtering options
8 files changed · +258 −20
java/org/apache/catalina/ha/session/ClusterManagerBase.java+3 −1 modified@@ -193,7 +193,9 @@ protected void clone(ClusterManagerBase copy) { copy.setMaxInactiveInterval(getMaxInactiveInterval()); copy.setProcessExpiresFrequency(getProcessExpiresFrequency()); copy.setNotifyListenersOnReplication(isNotifyListenersOnReplication()); - copy.setSessionAttributeFilter(getSessionAttributeFilter()); + copy.setSessionAttributeNameFilter(getSessionAttributeNameFilter()); + copy.setSessionAttributeValueClassNameFilter(getSessionAttributeValueClassNameFilter()); + copy.setWarnOnSessionAttributeFilterFailure(getWarnOnSessionAttributeFilterFailure()); copy.setSecureRandomClass(getSecureRandomClass()); copy.setSecureRandomProvider(getSecureRandomProvider()); copy.setSecureRandomAlgorithm(getSecureRandomAlgorithm());
java/org/apache/catalina/ha/session/mbeans-descriptors.xml+16 −0 modified@@ -345,6 +345,14 @@ name="sessionAttributeNameFilter" descritpion="The string pattern used for including session attributes in replication. Null means all attributes are included." type="java.lang.String"/> + <attribute + name="sessionAttributeValueClassNameFilter" + description="The regular expression used to filter session attributes based on the implementation class of the value. The regular expression is anchored and must match the fully qualified class name." + type="java.lang.String"/> + <attribute + name="warnOnSessionAttributeFilterFailure" + description="Should a WARN level log message be generated if a session attribute fails to match sessionAttributeNameFilter or sessionAttributeClassNameFilter?" + type="boolean"/> <operation name="expireSession" description="Expired the given session" @@ -576,6 +584,14 @@ name="sessionAttributeNameFilter" descritpion="The string pattern used for including session attributes in replication. Null means all attributes are included." type="java.lang.String"/> + <attribute + name="sessionAttributeValueClassNameFilter" + description="The regular expression used to filter session attributes based on the implementation class of the value. The regular expression is anchored and must match the fully qualified class name." + type="java.lang.String"/> + <attribute + name="warnOnSessionAttributeFilterFailure" + description="Should a WARN level log message be generated if a session attribute fails to match sessionAttributeNameFilter or sessionAttributeClassNameFilter?" + type="boolean"/> <operation name="expireSession" description="Expired the given session"
java/org/apache/catalina/session/LocalStrings.properties+2 −0 modified@@ -35,6 +35,8 @@ JDBCStore.commitSQLException=SQLException committing connection before closing managerBase.createRandom=Created random number generator for session ID generation in {0}ms. managerBase.contextNull=The Container must be set to a non-null Context instance before the Manager is used managerBase.createSession.ise=createSession: Too many active sessions +managerBase.sessionAttributeNameFilter=Skipped session attribute named [{0}] because it did not match the name filter [{1}] +managerBase.sessionAttributeValueClassNameFilter=Skipped session attribute named [{0}] because the value type [{1}] did not match the filter [{2}] managerBase.sessionTimeout=Invalid session timeout setting {0} managerBase.setContextNotNew=It is illegal to call setContext() to change the Context associated with a Manager if the Manager is not in the NEW state serverSession.value.iae=null value
java/org/apache/catalina/session/ManagerBase.java+116 −4 modified@@ -35,6 +35,7 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicLong; import java.util.regex.Pattern; +import java.util.regex.PatternSyntaxException; import org.apache.catalina.Container; import org.apache.catalina.Context; @@ -222,6 +223,10 @@ public abstract class ManagerBase extends LifecycleMBeanBase private Pattern sessionAttributeNamePattern; + private Pattern sessionAttributeValueClassNamePattern; + + private boolean warnOnSessionAttributeFilterFailure; + // ------------------------------------------------------------- Properties @@ -248,8 +253,86 @@ protected Pattern getSessionAttributeNamePattern() { /** - * Return the Container with which this Manager is associated. + * Obtain the regular expression used to filter session attribute based on + * the implementation class of the value. The regular expression is anchored + * and must match the fully qualified class name. + * + * @return The regular expression currently used to filter class names. + * {@code null} means no filter is applied. If an empty string is + * specified then no names will match the filter and all attributes + * will be blocked. + */ + public String getSessionAttributeValueClassNameFilter() { + if (sessionAttributeValueClassNamePattern == null) { + return null; + } + return sessionAttributeValueClassNamePattern.toString(); + } + + + /** + * Provides {@link #getSessionAttributeValueClassNameFilter()} as a + * pre-compiled regular expression pattern. + * + * @return The pre-compiled pattern used to filter session attributes based + * on the implementation class name of the value. {@code null} means + * no filter is applied. + */ + protected Pattern getSessionAttributeValueClassNamePattern() { + return sessionAttributeValueClassNamePattern; + } + + + /** + * Set the regular expression to use to filter classes used for session + * attributes. The regular expression is anchored and must match the fully + * qualified class name. + * + * @param sessionAttributeValueClassNameFilter The regular expression to use + * to filter session attributes based on class name. Use {@code + * null} if no filtering is required. If an empty string is + * specified then no names will match the filter and all + * attributes will be blocked. + * + * @throws PatternSyntaxException If the expression is not valid + */ + public void setSessionAttributeValueClassNameFilter(String sessionAttributeValueClassNameFilter) + throws PatternSyntaxException { + if (sessionAttributeValueClassNameFilter == null || + sessionAttributeValueClassNameFilter.length() == 0) { + sessionAttributeValueClassNamePattern = null; + } else { + sessionAttributeValueClassNamePattern = + Pattern.compile(sessionAttributeValueClassNameFilter); + } + } + + + /** + * Should a warn level log message be generated if a session attribute is + * not persisted / replicated / restored. + * + * @return {@code true} if a warn level log message should be generated + */ + public boolean getWarnOnSessionAttributeFilterFailure() { + return warnOnSessionAttributeFilterFailure; + } + + + /** + * Configure whether or not a warn level log message should be generated if + * a session attribute is not persisted / replicated / restored. + * + * @param warnOnSessionAttributeFilterFailure {@code true} if the + * warn level message should be generated + * */ + public void setWarnOnSessionAttributeFilterFailure( + boolean warnOnSessionAttributeFilterFailure) { + this.warnOnSessionAttributeFilterFailure = warnOnSessionAttributeFilterFailure; + } + + @Override public Container getContainer() { return this.container; @@ -767,10 +850,39 @@ public void changeSessionId(Session session) { @Override public boolean willAttributeDistribute(String name, Object value) { Pattern sessionAttributeNamePattern = getSessionAttributeNamePattern(); - if (sessionAttributeNamePattern == null) { - return true; + if (sessionAttributeNamePattern != null) { + if (!sessionAttributeNamePattern.matcher(name).matches()) { + if (getWarnOnSessionAttributeFilterFailure() || log.isDebugEnabled()) { + String msg = sm.getString("managerBase.sessionAttributeNameFilter", + name, sessionAttributeNamePattern); + if (getWarnOnSessionAttributeFilterFailure()) { + log.warn(msg); + } else { + log.debug(msg); + } + } + return false; + } } - return sessionAttributeNamePattern.matcher(name).matches(); + + Pattern sessionAttributeValueClassNamePattern = getSessionAttributeValueClassNamePattern(); + if (value != null && sessionAttributeValueClassNamePattern != null) { + if (!sessionAttributeValueClassNamePattern.matcher( + value.getClass().getName()).matches()) { + if (getWarnOnSessionAttributeFilterFailure() || log.isDebugEnabled()) { + String msg = sm.getString("managerBase.sessionAttributeValueClassNameFilter", + name, value.getClass().getName(), sessionAttributeNamePattern); + if (getWarnOnSessionAttributeFilterFailure()) { + log.warn(msg); + } else { + log.debug(msg); + } + } + return false; + } + } + + return true; }
java/org/apache/catalina/session/mbeans-descriptors.xml+16 −0 modified@@ -137,6 +137,14 @@ descritpion="The string pattern used for including session attributes in distribution. Null means all attributes are included." type="java.lang.String"/> + <attribute name="sessionAttributeValueClassNameFilter" + description="The regular expression used to filter session attributes based on the implementation class of the value. The regular expression is anchored and must match the fully qualified class name." + type="java.lang.String"/> + + <attribute name="warnOnSessionAttributeFilterFailure" + description="Should a WARN level log message be generated if a session attribute fails to match sessionAttributeNameFilter or sessionAttributeClassNameFilter?" + type="boolean"/> + <operation name="backgroundProcess" description="Invalidate all sessions that have expired." impact="ACTION" @@ -329,6 +337,14 @@ descritpion="The string pattern used for including session attributes in distribution. Null means all attributes are included." type="java.lang.String"/> + <attribute name="sessionAttributeValueClassNameFilter" + description="The regular expression used to filter session attributes based on the implementation class of the value. The regular expression is anchored and must match the fully qualified class name." + type="java.lang.String"/> + + <attribute name="warnOnSessionAttributeFilterFailure" + description="Should a WARN level log message be generated if a session attribute fails to match sessionAttributeNameFilter or sessionAttributeClassNameFilter?" + type="boolean"/> + <operation name="backgroundProcess" description="Invalidate all sessions that have expired." impact="ACTION"
webapps/docs/changelog.xml+7 −0 modified@@ -172,6 +172,13 @@ well as unload to ensure that configuration changes made while the web application is stopped are applied to any persisted data. (markt) </add> + <add> + Extend the session attribute filtering options to include filtering + based on the implementation class of the value and optional + <code>WARN</code> level logging if an attribute is filtered. These + options are avaialble for all of the Manager implementations that ship + with Tomcat. (markt) + </add> </changelog> </subsection> <subsection name="Jasper">
webapps/docs/config/cluster-manager.xml+56 −11 modified@@ -83,17 +83,6 @@ sessions to expire on all nodes when a shutdown occurs on one node, set this value to <code>true</code>. Default value is <code>false</code>. </attribute> - <attribute name="sessionAttributeNameFilter" required="false"> - A regular expression used to filter which session attributes will be - replicated. An attribute will only be replicated if its name matches - this pattern. If the pattern is zero length or <code>null</code>, all - attributes are eligible for replication. The pattern is anchored so the - session attribute name must fully match the pattern. As an example, the - value <code>(userName|sessionHistory)</code> will only replicate the - two session attributes named <code>userName</code> and - <code>sessionHistory</code>. If not specified, the default value of - <code>null</code> will be used. - </attribute> <attribute name="maxInactiveInterval" required="false"> <p>The initial maximum time interval, in seconds, between client requests before a session is invalidated. A negative value @@ -196,6 +185,26 @@ effective only when <code>sendAllSessions</code> is <code>false</code>. Default is <code>2000</code> milliseconds. </attribute> + <attribute name="sessionAttributeNameFilter" required="false"> + <p>A regular expression used to filter which session attributes will be + replicated. An attribute will only be replicated if its name matches + this pattern. If the pattern is zero length or <code>null</code>, all + attributes are eligible for replication. The pattern is anchored so the + session attribute name must fully match the pattern. As an example, the + value <code>(userName|sessionHistory)</code> will only replicate the + two session attributes named <code>userName</code> and + <code>sessionHistory</code>. If not specified, the default value of + <code>null</code> will be used.</p> + </attribute> + <attribute name="sessionAttributeValueClassNameFilter" required="false"> + <p>A regular expression used to filter which session attributes will be + replicated. An attribute will only be replicated if the implementation + class name of the value matches this pattern. If the pattern is zero + length or <code>null</code>, all attributes are eligible for + replication. The pattern is anchored so the fully qualified class name + must fully match the pattern. If not specified, the default value of + <code>null</code> will be used.</p> + </attribute> <attribute name="stateTimestampDrop" required="false"> When this node sends a <code>GET_ALL_SESSIONS</code> message to other node, all session messages that are received as a response are queued. @@ -207,6 +216,14 @@ If set to <code>false</code>, all queued session messages are handled. Default is <code>true</code>. </attribute> + <attribute name="warnOnSessionAttributeFilterFailure" required="false"> + <p>If <strong>sessionAttributeNameFilter</strong> or + <strong>sessionAttributeValueClassNameFilter</strong> blocks an + attribute, should this be logged at <code>WARN</code> level? If + <code>WARN</code> level logging is disabled then it will be logged at + <code>DEBUG</code>. The default value of this attribute is + <code>false</code>.</p> + </attribute> </attributes> </subsection> <subsection name="org.apache.catalina.ha.session.BackupManager Attributes"> @@ -230,13 +247,41 @@ another map. Default value is <code>15000</code> milliseconds. </attribute> + <attribute name="sessionAttributeNameFilter" required="false"> + <p>A regular expression used to filter which session attributes will be + replicated. An attribute will only be replicated if its name matches + this pattern. If the pattern is zero length or <code>null</code>, all + attributes are eligible for replication. The pattern is anchored so the + session attribute name must fully match the pattern. As an example, the + value <code>(userName|sessionHistory)</code> will only replicate the + two session attributes named <code>userName</code> and + <code>sessionHistory</code>. If not specified, the default value of + <code>null</code> will be used.</p> + </attribute> + <attribute name="sessionAttributeValueClassNameFilter" required="false"> + <p>A regular expression used to filter which session attributes will be + replicated. An attribute will only be replicated if the implementation + class name of the value matches this pattern. If the pattern is zero + length or <code>null</code>, all attributes are eligible for + replication. The pattern is anchored so the fully qualified class name + must fully match the pattern. If not specified, the default value of + <code>null</code> will be used.</p> + </attribute> <attribute name="terminateOnStartFailure" required="false"> Set to true if you wish to terminate replication map when replication map fails to start. If replication map is terminated, associated context will fail to start. If you set this attribute to false, replication map does not end. It will try to join the map membership in the heartbeat. Default value is <code>false</code> . </attribute> + <attribute name="warnOnSessionAttributeFilterFailure" required="false"> + <p>If <strong>sessionAttributeNameFilter</strong> or + <strong>sessionAttributeValueClassNameFilter</strong> blocks an + attribute, should this be logged at <code>WARN</code> level? If + <code>WARN</code> level logging is disabled then it will be logged at + <code>DEBUG</code>. The default value of this attribute is + <code>false</code>.</p> + </attribute> </attributes> </subsection> </section>
webapps/docs/config/manager.xml+42 −4 modified@@ -176,15 +176,34 @@ </attribute> <attribute name="sessionAttributeNameFilter" required="false"> - A regular expression used to filter which session attributes will be + <p>A regular expression used to filter which session attributes will be distributed. An attribute will only be distributed if its name matches this pattern. If the pattern is zero length or <code>null</code>, all attributes are eligible for distribution. The pattern is anchored so the session attribute name must fully match the pattern. As an example, the value <code>(userName|sessionHistory)</code> will only distribute the two session attributes named <code>userName</code> and <code>sessionHistory</code>. If not specified, the default value of - <code>null</code> will be used. + <code>null</code> will be used.</p> + </attribute> + + <attribute name="sessionAttributeValueClassNameFilter" required="false"> + <p>A regular expression used to filter which session attributes will be + distributed. An attribute will only be distributed if the implementation + class name of the value matches this pattern. If the pattern is zero + length or <code>null</code>, all attributes are eligible for + distribution. The pattern is anchored so the fully qualified class name + must fully match the pattern. If not specified, the default value of + <code>null</code> will be used.</p> + </attribute> + + <attribute name="warnOnSessionAttributeFilterFailure" required="false"> + <p>If <strong>sessionAttributeNameFilter</strong> or + <strong>sessionAttributeValueClassNameFilter</strong> blocks an + attribute, should this be logged at <code>WARN</code> level? If + <code>WARN</code> level logging is disabled then it will be logged at + <code>DEBUG</code>. The default value of this attribute is + <code>false</code>.</p> </attribute> </attributes> @@ -277,15 +296,34 @@ </attribute> <attribute name="sessionAttributeNameFilter" required="false"> - A regular expression used to filter which session attributes will be + <p>A regular expression used to filter which session attributes will be distributed. An attribute will only be distributed if its name matches this pattern. If the pattern is zero length or <code>null</code>, all attributes are eligible for distribution. The pattern is anchored so the session attribute name must fully match the pattern. As an example, the value <code>(userName|sessionHistory)</code> will only distribute the two session attributes named <code>userName</code> and <code>sessionHistory</code>. If not specified, the default value of - <code>null</code> will be used. + <code>null</code> will be used.</p> + </attribute> + + <attribute name="sessionAttributeValueClassNameFilter" required="false"> + <p>A regular expression used to filter which session attributes will be + distributed. An attribute will only be distributed if the implementation + class name of the value matches this pattern. If the pattern is zero + length or <code>null</code>, all attributes are eligible for + distribution. The pattern is anchored so the fully qualified class name + must fully match the pattern. If not specified, the default value of + <code>null</code> will be used.</p> + </attribute> + + <attribute name="warnOnSessionAttributeFilterFailure" required="false"> + <p>If <strong>sessionAttributeNameFilter</strong> or + <strong>sessionAttributeValueClassNameFilter</strong> blocks an + attribute, should this be logged at <code>WARN</code> level? If + <code>WARN</code> level logging is disabled then it will be logged at + <code>DEBUG</code>. The default value of this attribute is + <code>false</code>.</p> </attribute> </attributes>
2e5cc28052e8When using the new sessionAttributeValueClassNameFilter, apply the filter earlier rather than loading the class and then deciding to filter it out.
8 files changed · +154 −15
java/org/apache/catalina/session/ManagerBase.java+25 −1 modified@@ -37,6 +37,7 @@ import org.apache.catalina.Container; import org.apache.catalina.Context; import org.apache.catalina.Engine; +import org.apache.catalina.Globals; import org.apache.catalina.Lifecycle; import org.apache.catalina.LifecycleException; import org.apache.catalina.LifecycleState; @@ -226,8 +227,31 @@ public abstract class ManagerBase extends LifecycleMBeanBase implements Manager private boolean warnOnSessionAttributeFilterFailure; - // ------------------------------------------------------------- Properties + // ------------------------------------------------------------ Constructors + public ManagerBase() { + if (Globals.IS_SECURITY_ENABLED) { + // Minimum set required for default distribution/persistence to work + // plus String + setSessionAttributeValueClassNameFilter( + "java\\.lang\\.(?:Boolean|Integer|Long|Number|String)"); + setWarnOnSessionAttributeFilterFailure(true); + } + } + + + // -------------------------------------------------------------- Properties + + /** + * Obtain the regular expression used to filter session attribute based on + * attribute name. The regular expression is anchored so it must match the + * entire name + * + * @return The regular expression currently used to filter attribute names. + * {@code null} means no filter is applied. If an empty string is + * specified then no names will match the filter and all attributes + * will be blocked. + */ public String getSessionAttributeNameFilter() { if (sessionAttributeNamePattern == null) { return null;
java/org/apache/catalina/session/StandardManager.java+6 −2 modified@@ -191,10 +191,12 @@ protected void doLoad() throws ClassNotFoundException, IOException { } Loader loader = null; ClassLoader classLoader = null; + Log logger = null; try (FileInputStream fis = new FileInputStream(file.getAbsolutePath()); - BufferedInputStream bis = new BufferedInputStream(fis);) { + BufferedInputStream bis = new BufferedInputStream(fis)) { Context c = getContext(); loader = c.getLoader(); + logger = c.getLogger(); if (loader != null) { classLoader = loader.getClassLoader(); } @@ -204,7 +206,9 @@ protected void doLoad() throws ClassNotFoundException, IOException { // Load the previously unloaded active sessions synchronized (sessions) { - try (ObjectInputStream ois = new CustomObjectInputStream(bis, classLoader)) { + try (ObjectInputStream ois = new CustomObjectInputStream(bis, classLoader, logger, + getSessionAttributeValueClassNamePattern(), + getWarnOnSessionAttributeFilterFailure())) { Integer count = (Integer) ois.readObject(); int n = count.intValue(); if (log.isDebugEnabled())
java/org/apache/catalina/session/StoreBase.java+14 −1 modified@@ -215,7 +215,20 @@ public void processExpires() { */ protected ObjectInputStream getObjectInputStream(InputStream is) throws IOException { BufferedInputStream bis = new BufferedInputStream(is); - return new CustomObjectInputStream(bis, Thread.currentThread().getContextClassLoader()); + + CustomObjectInputStream ois; + ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); + + if (manager instanceof ManagerBase) { + ManagerBase managerBase = (ManagerBase) manager; + ois = new CustomObjectInputStream(bis, classLoader, manager.getContext().getLogger(), + managerBase.getSessionAttributeValueClassNamePattern(), + managerBase.getWarnOnSessionAttributeFilterFailure()); + } else { + ois = new CustomObjectInputStream(bis, classLoader); + } + + return ois; }
java/org/apache/catalina/util/CustomObjectInputStream.java+85 −2 modified@@ -18,9 +18,18 @@ import java.io.IOException; import java.io.InputStream; +import java.io.InvalidClassException; import java.io.ObjectInputStream; import java.io.ObjectStreamClass; import java.lang.reflect.Proxy; +import java.util.Collections; +import java.util.Set; +import java.util.WeakHashMap; +import java.util.concurrent.ConcurrentHashMap; +import java.util.regex.Pattern; + +import org.apache.juli.logging.Log; +import org.apache.tomcat.util.res.StringManager; /** * Custom subclass of <code>ObjectInputStream</code> that loads from the @@ -32,23 +41,81 @@ */ public final class CustomObjectInputStream extends ObjectInputStream { + private static final StringManager sm = StringManager.getManager(CustomObjectInputStream.class); + + private static final WeakHashMap<ClassLoader, Set<String>> reportedClassCache = + new WeakHashMap<>(); + /** * The class loader we will use to resolve classes. */ private final ClassLoader classLoader; + private final Set<String> reportedClasses; + private final Log log; + + private final Pattern allowedClassNamePattern; + private final String allowedClassNameFilter; + private final boolean warnOnFailure; /** - * Construct a new instance of CustomObjectInputStream + * Construct a new instance of CustomObjectInputStream without any filtering + * of deserialized classes. * * @param stream The input stream we will read from * @param classLoader The class loader used to instantiate objects * * @exception IOException if an input/output error occurs */ public CustomObjectInputStream(InputStream stream, ClassLoader classLoader) throws IOException { + this(stream, classLoader, null, null, false); + } + + + /** + * Construct a new instance of CustomObjectInputStream with filtering of + * deserialized classes. + * + * @param stream The input stream we will read from + * @param classLoader The class loader used to instantiate objects + * @param log The logger to use to report any issues. It may only be null if + * the filterMode does not require logging + * @param allowedClassNamePattern The regular expression to use to filter + * deserialized classes. The fully qualified + * class name must match this pattern for + * deserialization to be allowed if filtering + * is enabled. + * @param warnOnFailure Should any failures be logged? + * + * @exception IOException if an input/output error occurs + */ + public CustomObjectInputStream(InputStream stream, ClassLoader classLoader, + Log log, Pattern allowedClassNamePattern, boolean warnOnFailure) + throws IOException { super(stream); + if (log == null && allowedClassNamePattern != null && warnOnFailure) { + throw new IllegalArgumentException( + sm.getString("customObjectInputStream.logRequired")); + } this.classLoader = classLoader; + this.log = log; + this.allowedClassNamePattern = allowedClassNamePattern; + if (allowedClassNamePattern == null) { + this.allowedClassNameFilter = null; + } else { + this.allowedClassNameFilter = allowedClassNamePattern.toString(); + } + this.warnOnFailure = warnOnFailure; + + Set<String> reportedClasses; + synchronized (reportedClassCache) { + reportedClasses = reportedClassCache.get(classLoader); + if (reportedClasses == null) { + reportedClasses = Collections.newSetFromMap(new ConcurrentHashMap<String,Boolean>()); + reportedClassCache.put(classLoader, reportedClasses); + } + } + this.reportedClasses = reportedClasses; } @@ -64,8 +131,24 @@ public CustomObjectInputStream(InputStream stream, ClassLoader classLoader) thro @Override public Class<?> resolveClass(ObjectStreamClass classDesc) throws ClassNotFoundException, IOException { + + String name = classDesc.getName(); + if (allowedClassNamePattern != null) { + boolean allowed = allowedClassNamePattern.matcher(name).matches(); + if (!allowed) { + boolean doLog = warnOnFailure && reportedClasses.add(name); + String msg = sm.getString("customObjectInputStream.nomatch", name, allowedClassNameFilter); + if (doLog) { + log.warn(msg); + } else if (log.isDebugEnabled()) { + log.debug(msg); + } + throw new InvalidClassException(msg); + } + } + try { - return Class.forName(classDesc.getName(), false, classLoader); + return Class.forName(name, false, classLoader); } catch (ClassNotFoundException e) { try { // Try also the superclass because of primitive types
java/org/apache/catalina/util/LocalStrings.properties+2 −0 modified@@ -17,6 +17,8 @@ parameterMap.locked=No modifications are allowed to a locked ParameterMap resourceSet.locked=No modifications are allowed to a locked ResourceSet hexUtil.bad=Bad hexadecimal digit hexUtil.odd=Odd number of hexadecimal digits +customObjectInputStream.logRequired=A valid logger is required for class name filtering with logging +customObjectInputStream.nomatch=The class [{0}] did not match the regular expression [{1}] for classes allowed to be deserialized #Default Messages Utilized by the ExtensionValidator extensionValidator.web-application-manifest=Web Application Manifest extensionValidator.extension-not-found-error=ExtensionValidator[{0}][{1}]: Required extension [{2}] not found.
webapps/docs/changelog.xml+2 −1 modified@@ -166,7 +166,8 @@ based on the implementation class of the value and optional <code>WARN</code> level logging if an attribute is filtered. These options are avaialble for all of the Manager implementations that ship - with Tomcat. (markt) + with Tomcat. When a <code>SecurityManager</code> is used filtering will + be enabled by default. (markt) </add> </changelog> </subsection>
webapps/docs/config/cluster-manager.xml+10 −4 modified@@ -198,7 +198,9 @@ length or <code>null</code>, all attributes are eligible for replication. The pattern is anchored so the fully qualified class name must fully match the pattern. If not specified, the default value of - <code>null</code> will be used.</p> + <code>null</code> will be used unless a <code>SecurityManager</code> is + enabled in which case the default will be + <code>java\\.lang\\.(?:Boolean|Integer|Long|Number|String)</code>.</p> </attribute> <attribute name="stateTimestampDrop" required="false"> When this node sends a <code>GET_ALL_SESSIONS</code> message to other @@ -217,7 +219,8 @@ attribute, should this be logged at <code>WARN</code> level? If <code>WARN</code> level logging is disabled then it will be logged at <code>DEBUG</code>. The default value of this attribute is - <code>false</code>.</p> + <code>false</code> unless a <code>SecurityManager</code> is enabled in + which case the default will be <code>true</code>.</p> </attribute> </attributes> </subsection> @@ -260,7 +263,9 @@ length or <code>null</code>, all attributes are eligible for replication. The pattern is anchored so the fully qualified class name must fully match the pattern. If not specified, the default value of - <code>null</code> will be used.</p> + <code>null</code> will be used unless a <code>SecurityManager</code> is + enabled in which case the default will be + <code>java\\.lang\\.(?:Boolean|Integer|Long|Number|String)</code>.</p> </attribute> <attribute name="terminateOnStartFailure" required="false"> Set to true if you wish to terminate replication map when replication @@ -275,7 +280,8 @@ attribute, should this be logged at <code>WARN</code> level? If <code>WARN</code> level logging is disabled then it will be logged at <code>DEBUG</code>. The default value of this attribute is - <code>false</code>.</p> + <code>false</code> unless a <code>SecurityManager</code> is enabled in + which case the default will be <code>true</code>.</p> </attribute> </attributes> </subsection>
webapps/docs/config/manager.xml+10 −4 modified@@ -198,7 +198,9 @@ length or <code>null</code>, all attributes are eligible for distribution. The pattern is anchored so the fully qualified class name must fully match the pattern. If not specified, the default value of - <code>null</code> will be used.</p> + <code>null</code> will be used unless a <code>SecurityManager</code> is + enabled in which case the default will be + <code>java\\.lang\\.(?:Boolean|Integer|Long|Number|String)</code>.</p> </attribute> <attribute name="warnOnSessionAttributeFilterFailure" required="false"> @@ -207,7 +209,8 @@ attribute, should this be logged at <code>WARN</code> level? If <code>WARN</code> level logging is disabled then it will be logged at <code>DEBUG</code>. The default value of this attribute is - <code>false</code>.</p> + <code>false</code> unless a <code>SecurityManager</code> is enabled in + which case the default will be <code>true</code>.</p> </attribute> </attributes> @@ -318,7 +321,9 @@ length or <code>null</code>, all attributes are eligible for distribution. The pattern is anchored so the fully qualified class name must fully match the pattern. If not specified, the default value of - <code>null</code> will be used.</p> + <code>null</code> will be used unless a <code>SecurityManager</code> is + enabled in which case the default will be + <code>java\\.lang\\.(?:Boolean|Integer|Long|Number|String)</code>.</p> </attribute> <attribute name="warnOnSessionAttributeFilterFailure" required="false"> @@ -327,7 +332,8 @@ attribute, should this be logged at <code>WARN</code> level? If <code>WARN</code> level logging is disabled then it will be logged at <code>DEBUG</code>. The default value of this attribute is - <code>false</code>.</p> + <code>false</code> unless a <code>SecurityManager</code> is enabled in + which case the default will be <code>true</code>.</p> </attribute> </attributes>
5430f30c7938Expand the session attribute filtering options
8 files changed · +259 −18
java/org/apache/catalina/ha/session/ClusterManagerBase.java+3 −0 modified@@ -192,6 +192,9 @@ protected void clone(ClusterManagerBase copy) { copy.setProcessExpiresFrequency(getProcessExpiresFrequency()); copy.setNotifyListenersOnReplication(isNotifyListenersOnReplication()); copy.setSessionAttributeFilter(getSessionAttributeFilter()); + copy.setSessionAttributeNameFilter(getSessionAttributeNameFilter()); + copy.setSessionAttributeValueClassNameFilter(getSessionAttributeValueClassNameFilter()); + copy.setWarnOnSessionAttributeFilterFailure(getWarnOnSessionAttributeFilterFailure()); copy.setSecureRandomClass(getSecureRandomClass()); copy.setSecureRandomProvider(getSecureRandomProvider()); copy.setSecureRandomAlgorithm(getSecureRandomAlgorithm());
java/org/apache/catalina/ha/session/mbeans-descriptors.xml+16 −0 modified@@ -318,6 +318,14 @@ name="sessionAttributeNameFilter" descritpion="The string pattern used for including session attributes in replication. Null means all attributes are included." type="java.lang.String"/> + <attribute + name="sessionAttributeValueClassNameFilter" + description="The regular expression used to filter session attributes based on the implementation class of the value. The regular expression is anchored and must match the fully qualified class name." + type="java.lang.String"/> + <attribute + name="warnOnSessionAttributeFilterFailure" + description="Should a WARN level log message be generated if a session attribute fails to match sessionAttributeNameFilter or sessionAttributeClassNameFilter?" + type="boolean"/> <operation name="expireSession" description="Expired the given session" @@ -544,6 +552,14 @@ name="sessionAttributeNameFilter" descritpion="The string pattern used for including session attributes in replication. Null means all attributes are included." type="java.lang.String"/> + <attribute + name="sessionAttributeValueClassNameFilter" + description="The regular expression used to filter session attributes based on the implementation class of the value. The regular expression is anchored and must match the fully qualified class name." + type="java.lang.String"/> + <attribute + name="warnOnSessionAttributeFilterFailure" + description="Should a WARN level log message be generated if a session attribute fails to match sessionAttributeNameFilter or sessionAttributeClassNameFilter?" + type="boolean"/> <operation name="expireSession" description="Expired the given session"
java/org/apache/catalina/session/LocalStrings.properties+2 −0 modified@@ -33,6 +33,8 @@ JDBCStore.commitSQLException=SQLException committing connection before closing managerBase.container.noop=Managers added to containers other than Contexts will never be used managerBase.contextNull=The Context must be set to a non-null value before the Manager is used managerBase.createSession.ise=createSession: Too many active sessions +managerBase.sessionAttributeNameFilter=Skipped session attribute named [{0}] because it did not match the name filter [{1}] +managerBase.sessionAttributeValueClassNameFilter=Skipped session attribute named [{0}] because the value type [{1}] did not match the filter [{2}] managerBase.sessionTimeout=Invalid session timeout setting {0} managerBase.setContextNotNew=It is illegal to call setContext() to change the Context associated with a Manager if the Manager is not in the NEW state standardManager.loading=Loading persisted sessions from {0}
java/org/apache/catalina/session/ManagerBase.java+117 −3 modified@@ -33,6 +33,7 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicLong; import java.util.regex.Pattern; +import java.util.regex.PatternSyntaxException; import org.apache.catalina.Container; import org.apache.catalina.Context; @@ -214,6 +215,10 @@ public abstract class ManagerBase extends LifecycleMBeanBase private Pattern sessionAttributeNamePattern; + private Pattern sessionAttributeValueClassNamePattern; + + private boolean warnOnSessionAttributeFilterFailure; + // ------------------------------------------------------------- Properties @@ -238,6 +243,86 @@ protected Pattern getSessionAttributeNamePattern() { } + /** + * Obtain the regular expression used to filter session attribute based on + * the implementation class of the value. The regular expression is anchored + * and must match the fully qualified class name. + * + * @return The regular expression currently used to filter class names. + * {@code null} means no filter is applied. If an empty string is + * specified then no names will match the filter and all attributes + * will be blocked. + */ + public String getSessionAttributeValueClassNameFilter() { + if (sessionAttributeValueClassNamePattern == null) { + return null; + } + return sessionAttributeValueClassNamePattern.toString(); + } + + + /** + * Provides {@link #getSessionAttributeValueClassNameFilter()} as a + * pre-compiled regular expression pattern. + * + * @return The pre-compiled pattern used to filter session attributes based + * on the implementation class name of the value. {@code null} means + * no filter is applied. + */ + protected Pattern getSessionAttributeValueClassNamePattern() { + return sessionAttributeValueClassNamePattern; + } + + + /** + * Set the regular expression to use to filter classes used for session + * attributes. The regular expression is anchored and must match the fully + * qualified class name. + * + * @param sessionAttributeValueClassNameFilter The regular expression to use + * to filter session attributes based on class name. Use {@code + * null} if no filtering is required. If an empty string is + * specified then no names will match the filter and all + * attributes will be blocked. + * + * @throws PatternSyntaxException If the expression is not valid + */ + public void setSessionAttributeValueClassNameFilter(String sessionAttributeValueClassNameFilter) + throws PatternSyntaxException { + if (sessionAttributeValueClassNameFilter == null || + sessionAttributeValueClassNameFilter.length() == 0) { + sessionAttributeValueClassNamePattern = null; + } + sessionAttributeValueClassNamePattern = + Pattern.compile(sessionAttributeValueClassNameFilter); + } + + + /** + * Should a warn level log message be generated if a session attribute is + * not persisted / replicated / restored. + * + * @return {@code true} if a warn level log message should be generated + */ + public boolean getWarnOnSessionAttributeFilterFailure() { + return warnOnSessionAttributeFilterFailure; + } + + + /** + * Configure whether or not a warn level log message should be generated if + * a session attribute is not persisted / replicated / restored. + * + * @param warnOnSessionAttributeFilterFailure {@code true} if the + * warn level message should be generated + * + */ + public void setWarnOnSessionAttributeFilterFailure( + boolean warnOnSessionAttributeFilterFailure) { + this.warnOnSessionAttributeFilterFailure = warnOnSessionAttributeFilterFailure; + } + + @Override @Deprecated public Container getContainer() { @@ -784,10 +869,39 @@ protected void changeSessionId(Session session, String newId, @Override public boolean willAttributeDistribute(String name, Object value) { Pattern sessionAttributeNamePattern = getSessionAttributeNamePattern(); - if (sessionAttributeNamePattern == null) { - return true; + if (sessionAttributeNamePattern != null) { + if (!sessionAttributeNamePattern.matcher(name).matches()) { + if (getWarnOnSessionAttributeFilterFailure() || log.isDebugEnabled()) { + String msg = sm.getString("managerBase.sessionAttributeNameFilter", + name, sessionAttributeNamePattern); + if (getWarnOnSessionAttributeFilterFailure()) { + log.warn(msg); + } else { + log.debug(msg); + } + } + return false; + } } - return sessionAttributeNamePattern.matcher(name).matches(); + + Pattern sessionAttributeValueClassNamePattern = getSessionAttributeValueClassNamePattern(); + if (value != null && sessionAttributeValueClassNamePattern != null) { + if (!sessionAttributeValueClassNamePattern.matcher( + value.getClass().getName()).matches()) { + if (getWarnOnSessionAttributeFilterFailure() || log.isDebugEnabled()) { + String msg = sm.getString("managerBase.sessionAttributeValueClassNameFilter", + name, value.getClass().getName(), sessionAttributeNamePattern); + if (getWarnOnSessionAttributeFilterFailure()) { + log.warn(msg); + } else { + log.debug(msg); + } + } + return false; + } + } + + return true; }
java/org/apache/catalina/session/mbeans-descriptors.xml+16 −0 modified@@ -137,6 +137,14 @@ descritpion="The string pattern used for including session attributes in distribution. Null means all attributes are included." type="java.lang.String"/> + <attribute name="sessionAttributeValueClassNameFilter" + description="The regular expression used to filter session attributes based on the implementation class of the value. The regular expression is anchored and must match the fully qualified class name." + type="java.lang.String"/> + + <attribute name="warnOnSessionAttributeFilterFailure" + description="Should a WARN level log message be generated if a session attribute fails to match sessionAttributeNameFilter or sessionAttributeClassNameFilter?" + type="boolean"/> + <operation name="backgroundProcess" description="Invalidate all sessions that have expired." impact="ACTION" @@ -329,6 +337,14 @@ descritpion="The string pattern used for including session attributes in distribution. Null means all attributes are included." type="java.lang.String"/> + <attribute name="sessionAttributeValueClassNameFilter" + description="The regular expression used to filter session attributes based on the implementation class of the value. The regular expression is anchored and must match the fully qualified class name." + type="java.lang.String"/> + + <attribute name="warnOnSessionAttributeFilterFailure" + description="Should a WARN level log message be generated if a session attribute fails to match sessionAttributeNameFilter or sessionAttributeClassNameFilter?" + type="boolean"/> + <operation name="backgroundProcess" description="Invalidate all sessions that have expired." impact="ACTION"
webapps/docs/changelog.xml+7 −0 modified@@ -161,6 +161,13 @@ well as unload to ensure that configuration changes made while the web application is stopped are applied to any persisted data. (markt) </add> + <add> + Extend the session attribute filtering options to include filtering + based on the implementation class of the value and optional + <code>WARN</code> level logging if an attribute is filtered. These + options are avaialble for all of the Manager implementations that ship + with Tomcat. (markt) + </add> </changelog> </subsection> <subsection name="Jasper">
webapps/docs/config/cluster-manager.xml+56 −11 modified@@ -77,17 +77,6 @@ when session attributes are being replicated or removed across Tomcat nodes in the cluster. </attribute> - <attribute name="sessionAttributeNameFilter" required="false"> - A regular expression used to filter which session attributes will be - replicated. An attribute will only be replicated if its name matches - this pattern. If the pattern is zero length or <code>null</code>, all - attributes are eligible for replication. The pattern is anchored so the - session attribute name must fully match the pattern. As an example, the - value <code>(userName|sessionHistory)</code> will only replicate the - two session attributes named <code>userName</code> and - <code>sessionHistory</code>. If not specified, the default value of - <code>null</code> will be used. - </attribute> <attribute name="maxInactiveInterval" required="false"> <p>The initial maximum time interval, in seconds, between client requests before a session is invalidated. A negative value @@ -191,6 +180,26 @@ effective only when <code>sendAllSessions</code> is <code>false</code>. Default is <code>2000</code> milliseconds. </attribute> + <attribute name="sessionAttributeNameFilter" required="false"> + <p>A regular expression used to filter which session attributes will be + replicated. An attribute will only be replicated if its name matches + this pattern. If the pattern is zero length or <code>null</code>, all + attributes are eligible for replication. The pattern is anchored so the + session attribute name must fully match the pattern. As an example, the + value <code>(userName|sessionHistory)</code> will only replicate the + two session attributes named <code>userName</code> and + <code>sessionHistory</code>. If not specified, the default value of + <code>null</code> will be used.</p> + </attribute> + <attribute name="sessionAttributeValueClassNameFilter" required="false"> + <p>A regular expression used to filter which session attributes will be + replicated. An attribute will only be replicated if the implementation + class name of the value matches this pattern. If the pattern is zero + length or <code>null</code>, all attributes are eligible for + replication. The pattern is anchored so the fully qualified class name + must fully match the pattern. If not specified, the default value of + <code>null</code> will be used.</p> + </attribute> <attribute name="stateTimestampDrop" required="false"> When this node sends a <code>GET_ALL_SESSIONS</code> message to other node, all session messages that are received as a response are queued. @@ -202,6 +211,14 @@ If set to <code>false</code>, all queued session messages are handled. Default is <code>true</code>. </attribute> + <attribute name="warnOnSessionAttributeFilterFailure" required="false"> + <p>If <strong>sessionAttributeNameFilter</strong> or + <strong>sessionAttributeValueClassNameFilter</strong> blocks an + attribute, should this be logged at <code>WARN</code> level? If + <code>WARN</code> level logging is disabled then it will be logged at + <code>DEBUG</code>. The default value of this attribute is + <code>false</code>.</p> + </attribute> </attributes> </subsection> <subsection name="org.apache.catalina.ha.session.BackupManager Attributes"> @@ -225,13 +242,41 @@ another map. Default value is <code>15000</code> milliseconds. </attribute> + <attribute name="sessionAttributeNameFilter" required="false"> + <p>A regular expression used to filter which session attributes will be + replicated. An attribute will only be replicated if its name matches + this pattern. If the pattern is zero length or <code>null</code>, all + attributes are eligible for replication. The pattern is anchored so the + session attribute name must fully match the pattern. As an example, the + value <code>(userName|sessionHistory)</code> will only replicate the + two session attributes named <code>userName</code> and + <code>sessionHistory</code>. If not specified, the default value of + <code>null</code> will be used.</p> + </attribute> + <attribute name="sessionAttributeValueClassNameFilter" required="false"> + <p>A regular expression used to filter which session attributes will be + replicated. An attribute will only be replicated if the implementation + class name of the value matches this pattern. If the pattern is zero + length or <code>null</code>, all attributes are eligible for + replication. The pattern is anchored so the fully qualified class name + must fully match the pattern. If not specified, the default value of + <code>null</code> will be used.</p> + </attribute> <attribute name="terminateOnStartFailure" required="false"> Set to true if you wish to terminate replication map when replication map fails to start. If replication map is terminated, associated context will fail to start. If you set this attribute to false, replication map does not end. It will try to join the map membership in the heartbeat. Default value is <code>false</code> . </attribute> + <attribute name="warnOnSessionAttributeFilterFailure" required="false"> + <p>If <strong>sessionAttributeNameFilter</strong> or + <strong>sessionAttributeValueClassNameFilter</strong> blocks an + attribute, should this be logged at <code>WARN</code> level? If + <code>WARN</code> level logging is disabled then it will be logged at + <code>DEBUG</code>. The default value of this attribute is + <code>false</code>.</p> + </attribute> </attributes> </subsection> </section>
webapps/docs/config/manager.xml+42 −4 modified@@ -176,15 +176,34 @@ </attribute> <attribute name="sessionAttributeNameFilter" required="false"> - A regular expression used to filter which session attributes will be + <p>A regular expression used to filter which session attributes will be distributed. An attribute will only be distributed if its name matches this pattern. If the pattern is zero length or <code>null</code>, all attributes are eligible for distribution. The pattern is anchored so the session attribute name must fully match the pattern. As an example, the value <code>(userName|sessionHistory)</code> will only distribute the two session attributes named <code>userName</code> and <code>sessionHistory</code>. If not specified, the default value of - <code>null</code> will be used. + <code>null</code> will be used.</p> + </attribute> + + <attribute name="sessionAttributeValueClassNameFilter" required="false"> + <p>A regular expression used to filter which session attributes will be + distributed. An attribute will only be distributed if the implementation + class name of the value matches this pattern. If the pattern is zero + length or <code>null</code>, all attributes are eligible for + distribution. The pattern is anchored so the fully qualified class name + must fully match the pattern. If not specified, the default value of + <code>null</code> will be used.</p> + </attribute> + + <attribute name="warnOnSessionAttributeFilterFailure" required="false"> + <p>If <strong>sessionAttributeNameFilter</strong> or + <strong>sessionAttributeValueClassNameFilter</strong> blocks an + attribute, should this be logged at <code>WARN</code> level? If + <code>WARN</code> level logging is disabled then it will be logged at + <code>DEBUG</code>. The default value of this attribute is + <code>false</code>.</p> </attribute> </attributes> @@ -277,15 +296,34 @@ </attribute> <attribute name="sessionAttributeNameFilter" required="false"> - A regular expression used to filter which session attributes will be + <p>A regular expression used to filter which session attributes will be distributed. An attribute will only be distributed if its name matches this pattern. If the pattern is zero length or <code>null</code>, all attributes are eligible for distribution. The pattern is anchored so the session attribute name must fully match the pattern. As an example, the value <code>(userName|sessionHistory)</code> will only distribute the two session attributes named <code>userName</code> and <code>sessionHistory</code>. If not specified, the default value of - <code>null</code> will be used. + <code>null</code> will be used.</p> + </attribute> + + <attribute name="sessionAttributeValueClassNameFilter" required="false"> + <p>A regular expression used to filter which session attributes will be + distributed. An attribute will only be distributed if the implementation + class name of the value matches this pattern. If the pattern is zero + length or <code>null</code>, all attributes are eligible for + distribution. The pattern is anchored so the fully qualified class name + must fully match the pattern. If not specified, the default value of + <code>null</code> will be used.</p> + </attribute> + + <attribute name="warnOnSessionAttributeFilterFailure" required="false"> + <p>If <strong>sessionAttributeNameFilter</strong> or + <strong>sessionAttributeValueClassNameFilter</strong> blocks an + attribute, should this be logged at <code>WARN</code> level? If + <code>WARN</code> level logging is disabled then it will be logged at + <code>DEBUG</code>. The default value of this attribute is + <code>false</code>.</p> </attribute> </attributes>
e1b1002129feWhen using the new sessionAttributeValueClassNameFilter, apply the filter earlier rather than loading the class and then deciding to filter it out.
8 files changed · +146 −15
java/org/apache/catalina/session/ManagerBase.java+15 −1 modified@@ -37,6 +37,7 @@ import org.apache.catalina.Container; import org.apache.catalina.Context; import org.apache.catalina.Engine; +import org.apache.catalina.Globals; import org.apache.catalina.Lifecycle; import org.apache.catalina.LifecycleException; import org.apache.catalina.LifecycleState; @@ -193,7 +194,20 @@ public abstract class ManagerBase extends LifecycleMBeanBase implements Manager private boolean warnOnSessionAttributeFilterFailure; - // ------------------------------------------------------------- Properties + // ------------------------------------------------------------ Constructors + + public ManagerBase() { + if (Globals.IS_SECURITY_ENABLED) { + // Minimum set required for default distribution/persistence to work + // plus String + setSessionAttributeValueClassNameFilter( + "java\\.lang\\.(?:Boolean|Integer|Long|Number|String)"); + setWarnOnSessionAttributeFilterFailure(true); + } + } + + + // -------------------------------------------------------------- Properties /** * Obtain the regular expression used to filter session attribute based on
java/org/apache/catalina/session/StandardManager.java+6 −2 modified@@ -191,10 +191,12 @@ protected void doLoad() throws ClassNotFoundException, IOException { } Loader loader = null; ClassLoader classLoader = null; + Log logger = null; try (FileInputStream fis = new FileInputStream(file.getAbsolutePath()); - BufferedInputStream bis = new BufferedInputStream(fis);) { + BufferedInputStream bis = new BufferedInputStream(fis)) { Context c = getContext(); loader = c.getLoader(); + logger = c.getLogger(); if (loader != null) { classLoader = loader.getClassLoader(); } @@ -204,7 +206,9 @@ protected void doLoad() throws ClassNotFoundException, IOException { // Load the previously unloaded active sessions synchronized (sessions) { - try (ObjectInputStream ois = new CustomObjectInputStream(bis, classLoader)) { + try (ObjectInputStream ois = new CustomObjectInputStream(bis, classLoader, logger, + getSessionAttributeValueClassNamePattern(), + getWarnOnSessionAttributeFilterFailure())) { Integer count = (Integer) ois.readObject(); int n = count.intValue(); if (log.isDebugEnabled())
java/org/apache/catalina/session/StoreBase.java+14 −1 modified@@ -215,7 +215,20 @@ public void processExpires() { */ protected ObjectInputStream getObjectInputStream(InputStream is) throws IOException { BufferedInputStream bis = new BufferedInputStream(is); - return new CustomObjectInputStream(bis, Thread.currentThread().getContextClassLoader()); + + CustomObjectInputStream ois; + ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); + + if (manager instanceof ManagerBase) { + ManagerBase managerBase = (ManagerBase) manager; + ois = new CustomObjectInputStream(bis, classLoader, manager.getContext().getLogger(), + managerBase.getSessionAttributeValueClassNamePattern(), + managerBase.getWarnOnSessionAttributeFilterFailure()); + } else { + ois = new CustomObjectInputStream(bis, classLoader); + } + + return ois; }
java/org/apache/catalina/util/CustomObjectInputStream.java+87 −2 modified@@ -18,9 +18,18 @@ import java.io.IOException; import java.io.InputStream; +import java.io.InvalidClassException; import java.io.ObjectInputStream; import java.io.ObjectStreamClass; import java.lang.reflect.Proxy; +import java.util.Collections; +import java.util.Set; +import java.util.WeakHashMap; +import java.util.concurrent.ConcurrentHashMap; +import java.util.regex.Pattern; + +import org.apache.juli.logging.Log; +import org.apache.tomcat.util.res.StringManager; /** * Custom subclass of <code>ObjectInputStream</code> that loads from the @@ -32,23 +41,83 @@ */ public final class CustomObjectInputStream extends ObjectInputStream { + private static final StringManager sm = StringManager.getManager(CustomObjectInputStream.class); + + private static final WeakHashMap<ClassLoader, Set<String>> reportedClassCache = + new WeakHashMap<>(); + /** * The class loader we will use to resolve classes. */ private final ClassLoader classLoader; + private final Set<String> reportedClasses; + private final Log log; + + private final Pattern allowedClassNamePattern; + private final String allowedClassNameFilter; + private final boolean warnOnFailure; /** - * Construct a new instance of CustomObjectInputStream + * Construct a new instance of CustomObjectInputStream without any filtering + * of deserialized classes. * * @param stream The input stream we will read from * @param classLoader The class loader used to instantiate objects * * @exception IOException if an input/output error occurs */ public CustomObjectInputStream(InputStream stream, ClassLoader classLoader) throws IOException { + this(stream, classLoader, null, null, false); + } + + + /** + * Construct a new instance of CustomObjectInputStream with filtering of + * deserialized classes. + * + * @param stream The input stream we will read from + * @param classLoader The class loader used to instantiate objects + * @param log The logger to use to report any issues. It may only be null if + * the filterMode does not require logging + * @param allowedClassNamePattern The regular expression to use to filter + * deserialized classes. The fully qualified + * class name must match this pattern for + * deserialization to be allowed if filtering + * is enabled. + * @param warnOnFailure Should any failures be logged? + * + * @exception IOException if an input/output error occurs + */ + public CustomObjectInputStream(InputStream stream, ClassLoader classLoader, + Log log, Pattern allowedClassNamePattern, boolean warnOnFailure) + throws IOException { super(stream); + if (log == null && allowedClassNamePattern != null && warnOnFailure) { + throw new IllegalArgumentException( + sm.getString("customObjectInputStream.logRequired")); + } this.classLoader = classLoader; + this.log = log; + this.allowedClassNamePattern = allowedClassNamePattern; + if (allowedClassNamePattern == null) { + this.allowedClassNameFilter = null; + } else { + this.allowedClassNameFilter = allowedClassNamePattern.toString(); + } + this.warnOnFailure = warnOnFailure; + + Set<String> reportedClasses = reportedClassCache.get(classLoader); + if (reportedClasses == null) { + reportedClasses = Collections.newSetFromMap(new ConcurrentHashMap<>()); + Set<String> original = reportedClassCache.putIfAbsent(classLoader, reportedClasses); + if (original != null) { + // Concurrent attempts to create the new Set. Make sure all + // threads use the first successfully added Set. + reportedClasses = original; + } + } + this.reportedClasses = reportedClasses; } @@ -64,8 +133,24 @@ public CustomObjectInputStream(InputStream stream, ClassLoader classLoader) thro @Override public Class<?> resolveClass(ObjectStreamClass classDesc) throws ClassNotFoundException, IOException { + + String name = classDesc.getName(); + if (allowedClassNamePattern != null) { + boolean allowed = allowedClassNamePattern.matcher(name).matches(); + if (!allowed) { + boolean doLog = warnOnFailure && reportedClasses.add(name); + String msg = sm.getString("customObjectInputStream.nomatch", name, allowedClassNameFilter); + if (doLog) { + log.warn(msg); + } else if (log.isDebugEnabled()) { + log.debug(msg); + } + throw new InvalidClassException(msg); + } + } + try { - return Class.forName(classDesc.getName(), false, classLoader); + return Class.forName(name, false, classLoader); } catch (ClassNotFoundException e) { try { // Try also the superclass because of primitive types
java/org/apache/catalina/util/LocalStrings.properties+2 −0 modified@@ -17,6 +17,8 @@ parameterMap.locked=No modifications are allowed to a locked ParameterMap resourceSet.locked=No modifications are allowed to a locked ResourceSet hexUtil.bad=Bad hexadecimal digit hexUtil.odd=Odd number of hexadecimal digits +customObjectInputStream.logRequired=A valid logger is required for class name filtering with logging +customObjectInputStream.nomatch=The class [{0}] did not match the regular expression [{1}] for classes allowed to be deserialized #Default Messages Utilized by the ExtensionValidator extensionValidator.web-application-manifest=Web Application Manifest extensionValidator.extension-not-found-error=ExtensionValidator[{0}][{1}]: Required extension [{2}] not found.
webapps/docs/changelog.xml+2 −1 modified@@ -212,7 +212,8 @@ based on the implementation class of the value and optional <code>WARN</code> level logging if an attribute is filtered. These options are avaialble for all of the Manager implementations that ship - with Tomcat. (markt) + with Tomcat. When a <code>SecurityManager</code> is used filtering will + be enabled by default. (markt) </add> <scode> Remove <code>distributable</code> and <code>maxInactiveInterval</code>
webapps/docs/config/cluster-manager.xml+10 −4 modified@@ -180,7 +180,9 @@ length or <code>null</code>, all attributes are eligible for replication. The pattern is anchored so the fully qualified class name must fully match the pattern. If not specified, the default value of - <code>null</code> will be used.</p> + <code>null</code> will be used unless a <code>SecurityManager</code> is + enabled in which case the default will be + <code>java\\.lang\\.(?:Boolean|Integer|Long|Number|String)</code>.</p> </attribute> <attribute name="stateTimestampDrop" required="false"> When this node sends a <code>GET_ALL_SESSIONS</code> message to other @@ -199,7 +201,8 @@ attribute, should this be logged at <code>WARN</code> level? If <code>WARN</code> level logging is disabled then it will be logged at <code>DEBUG</code>. The default value of this attribute is - <code>false</code>.</p> + <code>false</code> unless a <code>SecurityManager</code> is enabled in + which case the default will be <code>true</code>.</p> </attribute> </attributes> </subsection> @@ -242,7 +245,9 @@ length or <code>null</code>, all attributes are eligible for replication. The pattern is anchored so the fully qualified class name must fully match the pattern. If not specified, the default value of - <code>null</code> will be used.</p> + <code>null</code> will be used unless a <code>SecurityManager</code> is + enabled in which case the default will be + <code>java\\.lang\\.(?:Boolean|Integer|Long|Number|String)</code>.</p> </attribute> <attribute name="terminateOnStartFailure" required="false"> Set to true if you wish to terminate replication map when replication @@ -257,7 +262,8 @@ attribute, should this be logged at <code>WARN</code> level? If <code>WARN</code> level logging is disabled then it will be logged at <code>DEBUG</code>. The default value of this attribute is - <code>false</code>.</p> + <code>false</code> unless a <code>SecurityManager</code> is enabled in + which case the default will be <code>true</code>.</p> </attribute> </attributes> </subsection>
webapps/docs/config/manager.xml+10 −4 modified@@ -159,7 +159,9 @@ length or <code>null</code>, all attributes are eligible for distribution. The pattern is anchored so the fully qualified class name must fully match the pattern. If not specified, the default value of - <code>null</code> will be used.</p> + <code>null</code> will be used unless a <code>SecurityManager</code> is + enabled in which case the default will be + <code>java\\.lang\\.(?:Boolean|Integer|Long|Number|String)</code>.</p> </attribute> <attribute name="warnOnSessionAttributeFilterFailure" required="false"> @@ -168,7 +170,8 @@ attribute, should this be logged at <code>WARN</code> level? If <code>WARN</code> level logging is disabled then it will be logged at <code>DEBUG</code>. The default value of this attribute is - <code>false</code>.</p> + <code>false</code> unless a <code>SecurityManager</code> is enabled in + which case the default will be <code>true</code>.</p> </attribute> </attributes> @@ -279,7 +282,9 @@ length or <code>null</code>, all attributes are eligible for distribution. The pattern is anchored so the fully qualified class name must fully match the pattern. If not specified, the default value of - <code>null</code> will be used.</p> + <code>null</code> will be used unless a <code>SecurityManager</code> is + enabled in which case the default will be + <code>java\\.lang\\.(?:Boolean|Integer|Long|Number|String)</code>.</p> </attribute> <attribute name="warnOnSessionAttributeFilterFailure" required="false"> @@ -288,7 +293,8 @@ attribute, should this be logged at <code>WARN</code> level? If <code>WARN</code> level logging is disabled then it will be logged at <code>DEBUG</code>. The default value of this attribute is - <code>false</code>.</p> + <code>false</code> unless a <code>SecurityManager</code> is enabled in + which case the default will be <code>true</code>.</p> </attribute> </attributes>
f626da75fd59When using the new sessionAttributeValueClassNameFilter, apply the filter earlier rather than loading the class and then deciding to filter it out.
8 files changed · +146 −15
java/org/apache/catalina/session/ManagerBase.java+15 −1 modified@@ -37,6 +37,7 @@ import org.apache.catalina.Container; import org.apache.catalina.Context; import org.apache.catalina.Engine; +import org.apache.catalina.Globals; import org.apache.catalina.Lifecycle; import org.apache.catalina.LifecycleException; import org.apache.catalina.LifecycleState; @@ -193,7 +194,20 @@ public abstract class ManagerBase extends LifecycleMBeanBase implements Manager private boolean warnOnSessionAttributeFilterFailure; - // ------------------------------------------------------------- Properties + // ------------------------------------------------------------ Constructors + + public ManagerBase() { + if (Globals.IS_SECURITY_ENABLED) { + // Minimum set required for default distribution/persistence to work + // plus String + setSessionAttributeValueClassNameFilter( + "java\\.lang\\.(?:Boolean|Integer|Long|Number|String)"); + setWarnOnSessionAttributeFilterFailure(true); + } + } + + + // -------------------------------------------------------------- Properties /** * Obtain the regular expression used to filter session attribute based on
java/org/apache/catalina/session/StandardManager.java+6 −2 modified@@ -191,10 +191,12 @@ protected void doLoad() throws ClassNotFoundException, IOException { } Loader loader = null; ClassLoader classLoader = null; + Log logger = null; try (FileInputStream fis = new FileInputStream(file.getAbsolutePath()); - BufferedInputStream bis = new BufferedInputStream(fis);) { + BufferedInputStream bis = new BufferedInputStream(fis)) { Context c = getContext(); loader = c.getLoader(); + logger = c.getLogger(); if (loader != null) { classLoader = loader.getClassLoader(); } @@ -204,7 +206,9 @@ protected void doLoad() throws ClassNotFoundException, IOException { // Load the previously unloaded active sessions synchronized (sessions) { - try (ObjectInputStream ois = new CustomObjectInputStream(bis, classLoader)) { + try (ObjectInputStream ois = new CustomObjectInputStream(bis, classLoader, logger, + getSessionAttributeValueClassNamePattern(), + getWarnOnSessionAttributeFilterFailure())) { Integer count = (Integer) ois.readObject(); int n = count.intValue(); if (log.isDebugEnabled())
java/org/apache/catalina/session/StoreBase.java+14 −1 modified@@ -215,7 +215,20 @@ public void processExpires() { */ protected ObjectInputStream getObjectInputStream(InputStream is) throws IOException { BufferedInputStream bis = new BufferedInputStream(is); - return new CustomObjectInputStream(bis, Thread.currentThread().getContextClassLoader()); + + CustomObjectInputStream ois; + ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); + + if (manager instanceof ManagerBase) { + ManagerBase managerBase = (ManagerBase) manager; + ois = new CustomObjectInputStream(bis, classLoader, manager.getContext().getLogger(), + managerBase.getSessionAttributeValueClassNamePattern(), + managerBase.getWarnOnSessionAttributeFilterFailure()); + } else { + ois = new CustomObjectInputStream(bis, classLoader); + } + + return ois; }
java/org/apache/catalina/util/CustomObjectInputStream.java+87 −2 modified@@ -18,9 +18,18 @@ import java.io.IOException; import java.io.InputStream; +import java.io.InvalidClassException; import java.io.ObjectInputStream; import java.io.ObjectStreamClass; import java.lang.reflect.Proxy; +import java.util.Collections; +import java.util.Set; +import java.util.WeakHashMap; +import java.util.concurrent.ConcurrentHashMap; +import java.util.regex.Pattern; + +import org.apache.juli.logging.Log; +import org.apache.tomcat.util.res.StringManager; /** * Custom subclass of <code>ObjectInputStream</code> that loads from the @@ -32,23 +41,83 @@ */ public final class CustomObjectInputStream extends ObjectInputStream { + private static final StringManager sm = StringManager.getManager(CustomObjectInputStream.class); + + private static final WeakHashMap<ClassLoader, Set<String>> reportedClassCache = + new WeakHashMap<>(); + /** * The class loader we will use to resolve classes. */ private final ClassLoader classLoader; + private final Set<String> reportedClasses; + private final Log log; + + private final Pattern allowedClassNamePattern; + private final String allowedClassNameFilter; + private final boolean warnOnFailure; /** - * Construct a new instance of CustomObjectInputStream + * Construct a new instance of CustomObjectInputStream without any filtering + * of deserialized classes. * * @param stream The input stream we will read from * @param classLoader The class loader used to instantiate objects * * @exception IOException if an input/output error occurs */ public CustomObjectInputStream(InputStream stream, ClassLoader classLoader) throws IOException { + this(stream, classLoader, null, null, false); + } + + + /** + * Construct a new instance of CustomObjectInputStream with filtering of + * deserialized classes. + * + * @param stream The input stream we will read from + * @param classLoader The class loader used to instantiate objects + * @param log The logger to use to report any issues. It may only be null if + * the filterMode does not require logging + * @param allowedClassNamePattern The regular expression to use to filter + * deserialized classes. The fully qualified + * class name must match this pattern for + * deserialization to be allowed if filtering + * is enabled. + * @param warnOnFailure Should any failures be logged? + * + * @exception IOException if an input/output error occurs + */ + public CustomObjectInputStream(InputStream stream, ClassLoader classLoader, + Log log, Pattern allowedClassNamePattern, boolean warnOnFailure) + throws IOException { super(stream); + if (log == null && allowedClassNamePattern != null && warnOnFailure) { + throw new IllegalArgumentException( + sm.getString("customObjectInputStream.logRequired")); + } this.classLoader = classLoader; + this.log = log; + this.allowedClassNamePattern = allowedClassNamePattern; + if (allowedClassNamePattern == null) { + this.allowedClassNameFilter = null; + } else { + this.allowedClassNameFilter = allowedClassNamePattern.toString(); + } + this.warnOnFailure = warnOnFailure; + + Set<String> reportedClasses = reportedClassCache.get(classLoader); + if (reportedClasses == null) { + reportedClasses = Collections.newSetFromMap(new ConcurrentHashMap<>()); + Set<String> original = reportedClassCache.putIfAbsent(classLoader, reportedClasses); + if (original != null) { + // Concurrent attempts to create the new Set. Make sure all + // threads use the first successfully added Set. + reportedClasses = original; + } + } + this.reportedClasses = reportedClasses; } @@ -64,8 +133,24 @@ public CustomObjectInputStream(InputStream stream, ClassLoader classLoader) thro @Override public Class<?> resolveClass(ObjectStreamClass classDesc) throws ClassNotFoundException, IOException { + + String name = classDesc.getName(); + if (allowedClassNamePattern != null) { + boolean allowed = allowedClassNamePattern.matcher(name).matches(); + if (!allowed) { + boolean doLog = warnOnFailure && reportedClasses.add(name); + String msg = sm.getString("customObjectInputStream.nomatch", name, allowedClassNameFilter); + if (doLog) { + log.warn(msg); + } else if (log.isDebugEnabled()) { + log.debug(msg); + } + throw new InvalidClassException(msg); + } + } + try { - return Class.forName(classDesc.getName(), false, classLoader); + return Class.forName(name, false, classLoader); } catch (ClassNotFoundException e) { try { // Try also the superclass because of primitive types
java/org/apache/catalina/util/LocalStrings.properties+2 −0 modified@@ -17,6 +17,8 @@ parameterMap.locked=No modifications are allowed to a locked ParameterMap resourceSet.locked=No modifications are allowed to a locked ResourceSet hexUtil.bad=Bad hexadecimal digit hexUtil.odd=Odd number of hexadecimal digits +customObjectInputStream.logRequired=A valid logger is required for class name filtering with logging +customObjectInputStream.nomatch=The class [{0}] did not match the regular expression [{1}] for classes allowed to be deserialized #Default Messages Utilized by the ExtensionValidator extensionValidator.web-application-manifest=Web Application Manifest extensionValidator.extension-not-found-error=ExtensionValidator[{0}][{1}]: Required extension [{2}] not found.
webapps/docs/changelog.xml+2 −1 modified@@ -212,7 +212,8 @@ based on the implementation class of the value and optional <code>WARN</code> level logging if an attribute is filtered. These options are avaialble for all of the Manager implementations that ship - with Tomcat. (markt) + with Tomcat. When a <code>SecurityManager</code> is used filtering will + be enabled by default. (markt) </add> <scode> Remove <code>distributable</code> and <code>maxInactiveInterval</code>
webapps/docs/config/cluster-manager.xml+10 −4 modified@@ -180,7 +180,9 @@ length or <code>null</code>, all attributes are eligible for replication. The pattern is anchored so the fully qualified class name must fully match the pattern. If not specified, the default value of - <code>null</code> will be used.</p> + <code>null</code> will be used unless a <code>SecurityManager</code> is + enabled in which case the default will be + <code>java\\.lang\\.(?:Boolean|Integer|Long|Number|String)</code>.</p> </attribute> <attribute name="stateTimestampDrop" required="false"> When this node sends a <code>GET_ALL_SESSIONS</code> message to other @@ -199,7 +201,8 @@ attribute, should this be logged at <code>WARN</code> level? If <code>WARN</code> level logging is disabled then it will be logged at <code>DEBUG</code>. The default value of this attribute is - <code>false</code>.</p> + <code>false</code> unless a <code>SecurityManager</code> is enabled in + which case the default will be <code>true</code>.</p> </attribute> </attributes> </subsection> @@ -242,7 +245,9 @@ length or <code>null</code>, all attributes are eligible for replication. The pattern is anchored so the fully qualified class name must fully match the pattern. If not specified, the default value of - <code>null</code> will be used.</p> + <code>null</code> will be used unless a <code>SecurityManager</code> is + enabled in which case the default will be + <code>java\\.lang\\.(?:Boolean|Integer|Long|Number|String)</code>.</p> </attribute> <attribute name="terminateOnStartFailure" required="false"> Set to true if you wish to terminate replication map when replication @@ -257,7 +262,8 @@ attribute, should this be logged at <code>WARN</code> level? If <code>WARN</code> level logging is disabled then it will be logged at <code>DEBUG</code>. The default value of this attribute is - <code>false</code>.</p> + <code>false</code> unless a <code>SecurityManager</code> is enabled in + which case the default will be <code>true</code>.</p> </attribute> </attributes> </subsection>
webapps/docs/config/manager.xml+10 −4 modified@@ -159,7 +159,9 @@ length or <code>null</code>, all attributes are eligible for distribution. The pattern is anchored so the fully qualified class name must fully match the pattern. If not specified, the default value of - <code>null</code> will be used.</p> + <code>null</code> will be used unless a <code>SecurityManager</code> is + enabled in which case the default will be + <code>java\\.lang\\.(?:Boolean|Integer|Long|Number|String)</code>.</p> </attribute> <attribute name="warnOnSessionAttributeFilterFailure" required="false"> @@ -168,7 +170,8 @@ attribute, should this be logged at <code>WARN</code> level? If <code>WARN</code> level logging is disabled then it will be logged at <code>DEBUG</code>. The default value of this attribute is - <code>false</code>.</p> + <code>false</code> unless a <code>SecurityManager</code> is enabled in + which case the default will be <code>true</code>.</p> </attribute> </attributes> @@ -279,7 +282,9 @@ length or <code>null</code>, all attributes are eligible for distribution. The pattern is anchored so the fully qualified class name must fully match the pattern. If not specified, the default value of - <code>null</code> will be used.</p> + <code>null</code> will be used unless a <code>SecurityManager</code> is + enabled in which case the default will be + <code>java\\.lang\\.(?:Boolean|Integer|Long|Number|String)</code>.</p> </attribute> <attribute name="warnOnSessionAttributeFilterFailure" required="false"> @@ -288,7 +293,8 @@ attribute, should this be logged at <code>WARN</code> level? If <code>WARN</code> level logging is disabled then it will be logged at <code>DEBUG</code>. The default value of this attribute is - <code>false</code>.</p> + <code>false</code> unless a <code>SecurityManager</code> is enabled in + which case the default will be <code>true</code>.</p> </attribute> </attributes>
824eb1d1ad92Expand the session attribute filtering options
7 files changed · +256 −18
java/org/apache/catalina/ha/session/mbeans-descriptors.xml+16 −0 modified@@ -314,6 +314,14 @@ name="sessionAttributeNameFilter" descritpion="The string pattern used for including session attributes in replication. Null means all attributes are included." type="java.lang.String"/> + <attribute + name="sessionAttributeValueClassNameFilter" + description="The regular expression used to filter session attributes based on the implementation class of the value. The regular expression is anchored and must match the fully qualified class name." + type="java.lang.String"/> + <attribute + name="warnOnSessionAttributeFilterFailure" + description="Should a WARN level log message be generated if a session attribute fails to match sessionAttributeNameFilter or sessionAttributeClassNameFilter?" + type="boolean"/> <operation name="expireSession" description="Expired the given session" @@ -536,6 +544,14 @@ name="sessionAttributeNameFilter" descritpion="The string pattern used for including session attributes in replication. Null means all attributes are included." type="java.lang.String"/> + <attribute + name="sessionAttributeValueClassNameFilter" + description="The regular expression used to filter session attributes based on the implementation class of the value. The regular expression is anchored and must match the fully qualified class name." + type="java.lang.String"/> + <attribute + name="warnOnSessionAttributeFilterFailure" + description="Should a WARN level log message be generated if a session attribute fails to match sessionAttributeNameFilter or sessionAttributeClassNameFilter?" + type="boolean"/> <operation name="expireSession" description="Expired the given session"
java/org/apache/catalina/session/LocalStrings.properties+2 −0 modified@@ -32,6 +32,8 @@ JDBCStore.missingDataSourceName=No valid JNDI name was given. JDBCStore.commitSQLException=SQLException committing connection before closing managerBase.container.noop=Managers added to containers other than Contexts will never be used managerBase.createSession.ise=createSession: Too many active sessions +managerBase.sessionAttributeNameFilter=Skipped session attribute named [{0}] because it did not match the name filter [{1}] +managerBase.sessionAttributeValueClassNameFilter=Skipped session attribute named [{0}] because the value type [{1}] did not match the filter [{2}] managerBase.sessionTimeout=Invalid session timeout setting {0} standardManager.loading=Loading persisted sessions from {0} standardManager.loading.exception=Exception while loading persisted sessions
java/org/apache/catalina/session/ManagerBase.java+116 −3 modified@@ -203,6 +203,10 @@ public abstract class ManagerBase extends LifecycleMBeanBase private Pattern sessionAttributeNamePattern; + private Pattern sessionAttributeValueClassNamePattern; + + private boolean warnOnSessionAttributeFilterFailure; + // ------------------------------------------------------------- Properties @@ -257,6 +261,86 @@ protected Pattern getSessionAttributeNamePattern() { } + /** + * Obtain the regular expression used to filter session attribute based on + * the implementation class of the value. The regular expression is anchored + * and must match the fully qualified class name. + * + * @return The regular expression currently used to filter class names. + * {@code null} means no filter is applied. If an empty string is + * specified then no names will match the filter and all attributes + * will be blocked. + */ + public String getSessionAttributeValueClassNameFilter() { + if (sessionAttributeValueClassNamePattern == null) { + return null; + } + return sessionAttributeValueClassNamePattern.toString(); + } + + + /** + * Provides {@link #getSessionAttributeValueClassNameFilter()} as a + * pre-compiled regular expression pattern. + * + * @return The pre-compiled pattern used to filter session attributes based + * on the implementation class name of the value. {@code null} means + * no filter is applied. + */ + protected Pattern getSessionAttributeValueClassNamePattern() { + return sessionAttributeValueClassNamePattern; + } + + + /** + * Set the regular expression to use to filter classes used for session + * attributes. The regular expression is anchored and must match the fully + * qualified class name. + * + * @param sessionAttributeValueClassNameFilter The regular expression to use + * to filter session attributes based on class name. Use {@code + * null} if no filtering is required. If an empty string is + * specified then no names will match the filter and all + * attributes will be blocked. + * + * @throws PatternSyntaxException If the expression is not valid + */ + public void setSessionAttributeValueClassNameFilter(String sessionAttributeValueClassNameFilter) + throws PatternSyntaxException { + if (sessionAttributeValueClassNameFilter == null || + sessionAttributeValueClassNameFilter.length() == 0) { + sessionAttributeValueClassNamePattern = null; + } + sessionAttributeValueClassNamePattern = + Pattern.compile(sessionAttributeValueClassNameFilter); + } + + + /** + * Should a warn level log message be generated if a session attribute is + * not persisted / replicated / restored. + * + * @return {@code true} if a warn level log message should be generated + */ + public boolean getWarnOnSessionAttributeFilterFailure() { + return warnOnSessionAttributeFilterFailure; + } + + + /** + * Configure whether or not a warn level log message should be generated if + * a session attribute is not persisted / replicated / restored. + * + * @param warnOnSessionAttributeFilterFailure {@code true} if the + * warn level message should be generated + * + */ + public void setWarnOnSessionAttributeFilterFailure( + boolean warnOnSessionAttributeFilterFailure) { + this.warnOnSessionAttributeFilterFailure = warnOnSessionAttributeFilterFailure; + } + + @Override public Context getContext() { return context; @@ -719,10 +803,39 @@ protected void changeSessionId(Session session, String newId, @Override public boolean willAttributeDistribute(String name, Object value) { Pattern sessionAttributeNamePattern = getSessionAttributeNamePattern(); - if (sessionAttributeNamePattern == null) { - return true; + if (sessionAttributeNamePattern != null) { + if (!sessionAttributeNamePattern.matcher(name).matches()) { + if (getWarnOnSessionAttributeFilterFailure() || log.isDebugEnabled()) { + String msg = sm.getString("managerBase.sessionAttributeNameFilter", + name, sessionAttributeNamePattern); + if (getWarnOnSessionAttributeFilterFailure()) { + log.warn(msg); + } else { + log.debug(msg); + } + } + return false; + } + } + + Pattern sessionAttributeValueClassNamePattern = getSessionAttributeValueClassNamePattern(); + if (value != null && sessionAttributeValueClassNamePattern != null) { + if (!sessionAttributeValueClassNamePattern.matcher( + value.getClass().getName()).matches()) { + if (getWarnOnSessionAttributeFilterFailure() || log.isDebugEnabled()) { + String msg = sm.getString("managerBase.sessionAttributeValueClassNameFilter", + name, value.getClass().getName(), sessionAttributeNamePattern); + if (getWarnOnSessionAttributeFilterFailure()) { + log.warn(msg); + } else { + log.debug(msg); + } + } + return false; + } } - return sessionAttributeNamePattern.matcher(name).matches(); + + return true; }
java/org/apache/catalina/session/mbeans-descriptors.xml+16 −0 modified@@ -132,6 +132,14 @@ descritpion="The string pattern used for including session attributes in distribution. Null means all attributes are included." type="java.lang.String"/> + <attribute name="sessionAttributeValueClassNameFilter" + description="The regular expression used to filter session attributes based on the implementation class of the value. The regular expression is anchored and must match the fully qualified class name." + type="java.lang.String"/> + + <attribute name="warnOnSessionAttributeFilterFailure" + description="Should a WARN level log message be generated if a session attribute fails to match sessionAttributeNameFilter or sessionAttributeClassNameFilter?" + type="boolean"/> + <operation name="backgroundProcess" description="Invalidate all sessions that have expired." impact="ACTION" @@ -319,6 +327,14 @@ descritpion="The string pattern used for including session attributes in distribution. Null means all attributes are included." type="java.lang.String"/> + <attribute name="sessionAttributeValueClassNameFilter" + description="The regular expression used to filter session attributes based on the implementation class of the value. The regular expression is anchored and must match the fully qualified class name." + type="java.lang.String"/> + + <attribute name="warnOnSessionAttributeFilterFailure" + description="Should a WARN level log message be generated if a session attribute fails to match sessionAttributeNameFilter or sessionAttributeClassNameFilter?" + type="boolean"/> + <operation name="backgroundProcess" description="Invalidate all sessions that have expired." impact="ACTION"
webapps/docs/changelog.xml+7 −0 modified@@ -207,6 +207,13 @@ well as unload to ensure that configuration changes made while the web application is stopped are applied to any persisted data. (markt) </add> + <add> + Extend the session attribute filtering options to include filtering + based on the implementation class of the value and optional + <code>WARN</code> level logging if an attribute is filtered. These + options are avaialble for all of the Manager implementations that ship + with Tomcat. (markt) + </add> </changelog> </subsection> <subsection name="Coyote">
webapps/docs/config/cluster-manager.xml+56 −11 modified@@ -77,17 +77,6 @@ when session attributes are being replicated or removed across Tomcat nodes in the cluster. </attribute> - <attribute name="sessionAttributeNameFilter" required="false"> - A regular expression used to filter which session attributes will be - replicated. An attribute will only be replicated if its name matches - this pattern. If the pattern is zero length or <code>null</code>, all - attributes are eligible for replication. The pattern is anchored so the - session attribute name must fully match the pattern. As an example, the - value <code>(userName|sessionHistory)</code> will only replicate the - two session attributes named <code>userName</code> and - <code>sessionHistory</code>. If not specified, the default value of - <code>null</code> will be used. - </attribute> <attribute name="maxInactiveInterval" required="false"> <p>The initial maximum time interval, in seconds, between client requests before a session is invalidated. A negative value @@ -184,6 +173,26 @@ effective only when <code>sendAllSessions</code> is <code>false</code>. Default is <code>2000</code> milliseconds. </attribute> + <attribute name="sessionAttributeNameFilter" required="false"> + <p>A regular expression used to filter which session attributes will be + replicated. An attribute will only be replicated if its name matches + this pattern. If the pattern is zero length or <code>null</code>, all + attributes are eligible for replication. The pattern is anchored so the + session attribute name must fully match the pattern. As an example, the + value <code>(userName|sessionHistory)</code> will only replicate the + two session attributes named <code>userName</code> and + <code>sessionHistory</code>. If not specified, the default value of + <code>null</code> will be used.</p> + </attribute> + <attribute name="sessionAttributeValueClassNameFilter" required="false"> + <p>A regular expression used to filter which session attributes will be + replicated. An attribute will only be replicated if the implementation + class name of the value matches this pattern. If the pattern is zero + length or <code>null</code>, all attributes are eligible for + replication. The pattern is anchored so the fully qualified class name + must fully match the pattern. If not specified, the default value of + <code>null</code> will be used.</p> + </attribute> <attribute name="stateTimestampDrop" required="false"> When this node sends a <code>GET_ALL_SESSIONS</code> message to other node, all session messages that are received as a response are queued. @@ -195,6 +204,14 @@ If set to <code>false</code>, all queued session messages are handled. Default is <code>true</code>. </attribute> + <attribute name="warnOnSessionAttributeFilterFailure" required="false"> + <p>If <strong>sessionAttributeNameFilter</strong> or + <strong>sessionAttributeValueClassNameFilter</strong> blocks an + attribute, should this be logged at <code>WARN</code> level? If + <code>WARN</code> level logging is disabled then it will be logged at + <code>DEBUG</code>. The default value of this attribute is + <code>false</code>.</p> + </attribute> </attributes> </subsection> <subsection name="org.apache.catalina.ha.session.BackupManager Attributes"> @@ -218,13 +235,41 @@ another map. Default value is <code>15000</code> milliseconds. </attribute> + <attribute name="sessionAttributeNameFilter" required="false"> + <p>A regular expression used to filter which session attributes will be + replicated. An attribute will only be replicated if its name matches + this pattern. If the pattern is zero length or <code>null</code>, all + attributes are eligible for replication. The pattern is anchored so the + session attribute name must fully match the pattern. As an example, the + value <code>(userName|sessionHistory)</code> will only replicate the + two session attributes named <code>userName</code> and + <code>sessionHistory</code>. If not specified, the default value of + <code>null</code> will be used.</p> + </attribute> + <attribute name="sessionAttributeValueClassNameFilter" required="false"> + <p>A regular expression used to filter which session attributes will be + replicated. An attribute will only be replicated if the implementation + class name of the value matches this pattern. If the pattern is zero + length or <code>null</code>, all attributes are eligible for + replication. The pattern is anchored so the fully qualified class name + must fully match the pattern. If not specified, the default value of + <code>null</code> will be used.</p> + </attribute> <attribute name="terminateOnStartFailure" required="false"> Set to true if you wish to terminate replication map when replication map fails to start. If replication map is terminated, associated context will fail to start. If you set this attribute to false, replication map does not end. It will try to join the map membership in the heartbeat. Default value is <code>false</code> . </attribute> + <attribute name="warnOnSessionAttributeFilterFailure" required="false"> + <p>If <strong>sessionAttributeNameFilter</strong> or + <strong>sessionAttributeValueClassNameFilter</strong> blocks an + attribute, should this be logged at <code>WARN</code> level? If + <code>WARN</code> level logging is disabled then it will be logged at + <code>DEBUG</code>. The default value of this attribute is + <code>false</code>.</p> + </attribute> </attributes> </subsection> </section>
webapps/docs/config/manager.xml+43 −4 modified@@ -167,15 +167,34 @@ </attribute> <attribute name="sessionAttributeNameFilter" required="false"> - A regular expression used to filter which session attributes will be + <p>A regular expression used to filter which session attributes will be distributed. An attribute will only be distributed if its name matches this pattern. If the pattern is zero length or <code>null</code>, all attributes are eligible for distribution. The pattern is anchored so the session attribute name must fully match the pattern. As an example, the value <code>(userName|sessionHistory)</code> will only distribute the two session attributes named <code>userName</code> and <code>sessionHistory</code>. If not specified, the default value of - <code>null</code> will be used. + <code>null</code> will be used.</p> + </attribute> + + <attribute name="sessionAttributeValueClassNameFilter" required="false"> + <p>A regular expression used to filter which session attributes will be + distributed. An attribute will only be distributed if the implementation + class name of the value matches this pattern. If the pattern is zero + length or <code>null</code>, all attributes are eligible for + distribution. The pattern is anchored so the fully qualified class name + must fully match the pattern. If not specified, the default value of + <code>null</code> will be used.</p> + </attribute> + + <attribute name="warnOnSessionAttributeFilterFailure" required="false"> + <p>If <strong>sessionAttributeNameFilter</strong> or + <strong>sessionAttributeValueClassNameFilter</strong> blocks an + attribute, should this be logged at <code>WARN</code> level? If + <code>WARN</code> level logging is disabled then it will be logged at + <code>DEBUG</code>. The default value of this attribute is + <code>false</code>.</p> </attribute> </attributes> @@ -266,16 +285,36 @@ <code>org.apache.catalina.session.StandardManager</code> class. </p> </attribute> + <attribute name="sessionAttributeNameFilter" required="false"> - A regular expression used to filter which session attributes will be + <p>A regular expression used to filter which session attributes will be distributed. An attribute will only be distributed if its name matches this pattern. If the pattern is zero length or <code>null</code>, all attributes are eligible for distribution. The pattern is anchored so the session attribute name must fully match the pattern. As an example, the value <code>(userName|sessionHistory)</code> will only distribute the two session attributes named <code>userName</code> and <code>sessionHistory</code>. If not specified, the default value of - <code>null</code> will be used. + <code>null</code> will be used.</p> + </attribute> + + <attribute name="sessionAttributeValueClassNameFilter" required="false"> + <p>A regular expression used to filter which session attributes will be + distributed. An attribute will only be distributed if the implementation + class name of the value matches this pattern. If the pattern is zero + length or <code>null</code>, all attributes are eligible for + distribution. The pattern is anchored so the fully qualified class name + must fully match the pattern. If not specified, the default value of + <code>null</code> will be used.</p> + </attribute> + + <attribute name="warnOnSessionAttributeFilterFailure" required="false"> + <p>If <strong>sessionAttributeNameFilter</strong> or + <strong>sessionAttributeValueClassNameFilter</strong> blocks an + attribute, should this be logged at <code>WARN</code> level? If + <code>WARN</code> level logging is disabled then it will be logged at + <code>DEBUG</code>. The default value of this attribute is + <code>false</code>.</p> </attribute> </attributes>
50f1b1da794cExpand the session attribute filtering options
7 files changed · +256 −18
java/org/apache/catalina/ha/session/mbeans-descriptors.xml+16 −0 modified@@ -314,6 +314,14 @@ name="sessionAttributeNameFilter" descritpion="The string pattern used for including session attributes in replication. Null means all attributes are included." type="java.lang.String"/> + <attribute + name="sessionAttributeValueClassNameFilter" + description="The regular expression used to filter session attributes based on the implementation class of the value. The regular expression is anchored and must match the fully qualified class name." + type="java.lang.String"/> + <attribute + name="warnOnSessionAttributeFilterFailure" + description="Should a WARN level log message be generated if a session attribute fails to match sessionAttributeNameFilter or sessionAttributeClassNameFilter?" + type="boolean"/> <operation name="expireSession" description="Expired the given session" @@ -536,6 +544,14 @@ name="sessionAttributeNameFilter" descritpion="The string pattern used for including session attributes in replication. Null means all attributes are included." type="java.lang.String"/> + <attribute + name="sessionAttributeValueClassNameFilter" + description="The regular expression used to filter session attributes based on the implementation class of the value. The regular expression is anchored and must match the fully qualified class name." + type="java.lang.String"/> + <attribute + name="warnOnSessionAttributeFilterFailure" + description="Should a WARN level log message be generated if a session attribute fails to match sessionAttributeNameFilter or sessionAttributeClassNameFilter?" + type="boolean"/> <operation name="expireSession" description="Expired the given session"
java/org/apache/catalina/session/LocalStrings.properties+2 −0 modified@@ -32,6 +32,8 @@ JDBCStore.missingDataSourceName=No valid JNDI name was given. JDBCStore.commitSQLException=SQLException committing connection before closing managerBase.container.noop=Managers added to containers other than Contexts will never be used managerBase.createSession.ise=createSession: Too many active sessions +managerBase.sessionAttributeNameFilter=Skipped session attribute named [{0}] because it did not match the name filter [{1}] +managerBase.sessionAttributeValueClassNameFilter=Skipped session attribute named [{0}] because the value type [{1}] did not match the filter [{2}] managerBase.sessionTimeout=Invalid session timeout setting {0} standardManager.loading=Loading persisted sessions from {0} standardManager.loading.exception=Exception while loading persisted sessions
java/org/apache/catalina/session/ManagerBase.java+116 −3 modified@@ -203,6 +203,10 @@ public abstract class ManagerBase extends LifecycleMBeanBase private Pattern sessionAttributeNamePattern; + private Pattern sessionAttributeValueClassNamePattern; + + private boolean warnOnSessionAttributeFilterFailure; + // ------------------------------------------------------------- Properties @@ -257,6 +261,86 @@ protected Pattern getSessionAttributeNamePattern() { } + /** + * Obtain the regular expression used to filter session attribute based on + * the implementation class of the value. The regular expression is anchored + * and must match the fully qualified class name. + * + * @return The regular expression currently used to filter class names. + * {@code null} means no filter is applied. If an empty string is + * specified then no names will match the filter and all attributes + * will be blocked. + */ + public String getSessionAttributeValueClassNameFilter() { + if (sessionAttributeValueClassNamePattern == null) { + return null; + } + return sessionAttributeValueClassNamePattern.toString(); + } + + + /** + * Provides {@link #getSessionAttributeValueClassNameFilter()} as a + * pre-compiled regular expression pattern. + * + * @return The pre-compiled pattern used to filter session attributes based + * on the implementation class name of the value. {@code null} means + * no filter is applied. + */ + protected Pattern getSessionAttributeValueClassNamePattern() { + return sessionAttributeValueClassNamePattern; + } + + + /** + * Set the regular expression to use to filter classes used for session + * attributes. The regular expression is anchored and must match the fully + * qualified class name. + * + * @param sessionAttributeValueClassNameFilter The regular expression to use + * to filter session attributes based on class name. Use {@code + * null} if no filtering is required. If an empty string is + * specified then no names will match the filter and all + * attributes will be blocked. + * + * @throws PatternSyntaxException If the expression is not valid + */ + public void setSessionAttributeValueClassNameFilter(String sessionAttributeValueClassNameFilter) + throws PatternSyntaxException { + if (sessionAttributeValueClassNameFilter == null || + sessionAttributeValueClassNameFilter.length() == 0) { + sessionAttributeValueClassNamePattern = null; + } + sessionAttributeValueClassNamePattern = + Pattern.compile(sessionAttributeValueClassNameFilter); + } + + + /** + * Should a warn level log message be generated if a session attribute is + * not persisted / replicated / restored. + * + * @return {@code true} if a warn level log message should be generated + */ + public boolean getWarnOnSessionAttributeFilterFailure() { + return warnOnSessionAttributeFilterFailure; + } + + + /** + * Configure whether or not a warn level log message should be generated if + * a session attribute is not persisted / replicated / restored. + * + * @param warnOnSessionAttributeFilterFailure {@code true} if the + * warn level message should be generated + * + */ + public void setWarnOnSessionAttributeFilterFailure( + boolean warnOnSessionAttributeFilterFailure) { + this.warnOnSessionAttributeFilterFailure = warnOnSessionAttributeFilterFailure; + } + + @Override public Context getContext() { return context; @@ -719,10 +803,39 @@ protected void changeSessionId(Session session, String newId, @Override public boolean willAttributeDistribute(String name, Object value) { Pattern sessionAttributeNamePattern = getSessionAttributeNamePattern(); - if (sessionAttributeNamePattern == null) { - return true; + if (sessionAttributeNamePattern != null) { + if (!sessionAttributeNamePattern.matcher(name).matches()) { + if (getWarnOnSessionAttributeFilterFailure() || log.isDebugEnabled()) { + String msg = sm.getString("managerBase.sessionAttributeNameFilter", + name, sessionAttributeNamePattern); + if (getWarnOnSessionAttributeFilterFailure()) { + log.warn(msg); + } else { + log.debug(msg); + } + } + return false; + } + } + + Pattern sessionAttributeValueClassNamePattern = getSessionAttributeValueClassNamePattern(); + if (value != null && sessionAttributeValueClassNamePattern != null) { + if (!sessionAttributeValueClassNamePattern.matcher( + value.getClass().getName()).matches()) { + if (getWarnOnSessionAttributeFilterFailure() || log.isDebugEnabled()) { + String msg = sm.getString("managerBase.sessionAttributeValueClassNameFilter", + name, value.getClass().getName(), sessionAttributeNamePattern); + if (getWarnOnSessionAttributeFilterFailure()) { + log.warn(msg); + } else { + log.debug(msg); + } + } + return false; + } } - return sessionAttributeNamePattern.matcher(name).matches(); + + return true; }
java/org/apache/catalina/session/mbeans-descriptors.xml+16 −0 modified@@ -132,6 +132,14 @@ descritpion="The string pattern used for including session attributes in distribution. Null means all attributes are included." type="java.lang.String"/> + <attribute name="sessionAttributeValueClassNameFilter" + description="The regular expression used to filter session attributes based on the implementation class of the value. The regular expression is anchored and must match the fully qualified class name." + type="java.lang.String"/> + + <attribute name="warnOnSessionAttributeFilterFailure" + description="Should a WARN level log message be generated if a session attribute fails to match sessionAttributeNameFilter or sessionAttributeClassNameFilter?" + type="boolean"/> + <operation name="backgroundProcess" description="Invalidate all sessions that have expired." impact="ACTION" @@ -319,6 +327,14 @@ descritpion="The string pattern used for including session attributes in distribution. Null means all attributes are included." type="java.lang.String"/> + <attribute name="sessionAttributeValueClassNameFilter" + description="The regular expression used to filter session attributes based on the implementation class of the value. The regular expression is anchored and must match the fully qualified class name." + type="java.lang.String"/> + + <attribute name="warnOnSessionAttributeFilterFailure" + description="Should a WARN level log message be generated if a session attribute fails to match sessionAttributeNameFilter or sessionAttributeClassNameFilter?" + type="boolean"/> + <operation name="backgroundProcess" description="Invalidate all sessions that have expired." impact="ACTION"
webapps/docs/changelog.xml+7 −0 modified@@ -207,6 +207,13 @@ well as unload to ensure that configuration changes made while the web application is stopped are applied to any persisted data. (markt) </add> + <add> + Extend the session attribute filtering options to include filtering + based on the implementation class of the value and optional + <code>WARN</code> level logging if an attribute is filtered. These + options are avaialble for all of the Manager implementations that ship + with Tomcat. (markt) + </add> </changelog> </subsection> <subsection name="Coyote">
webapps/docs/config/cluster-manager.xml+56 −11 modified@@ -77,17 +77,6 @@ when session attributes are being replicated or removed across Tomcat nodes in the cluster. </attribute> - <attribute name="sessionAttributeNameFilter" required="false"> - A regular expression used to filter which session attributes will be - replicated. An attribute will only be replicated if its name matches - this pattern. If the pattern is zero length or <code>null</code>, all - attributes are eligible for replication. The pattern is anchored so the - session attribute name must fully match the pattern. As an example, the - value <code>(userName|sessionHistory)</code> will only replicate the - two session attributes named <code>userName</code> and - <code>sessionHistory</code>. If not specified, the default value of - <code>null</code> will be used. - </attribute> <attribute name="maxInactiveInterval" required="false"> <p>The initial maximum time interval, in seconds, between client requests before a session is invalidated. A negative value @@ -184,6 +173,26 @@ effective only when <code>sendAllSessions</code> is <code>false</code>. Default is <code>2000</code> milliseconds. </attribute> + <attribute name="sessionAttributeNameFilter" required="false"> + <p>A regular expression used to filter which session attributes will be + replicated. An attribute will only be replicated if its name matches + this pattern. If the pattern is zero length or <code>null</code>, all + attributes are eligible for replication. The pattern is anchored so the + session attribute name must fully match the pattern. As an example, the + value <code>(userName|sessionHistory)</code> will only replicate the + two session attributes named <code>userName</code> and + <code>sessionHistory</code>. If not specified, the default value of + <code>null</code> will be used.</p> + </attribute> + <attribute name="sessionAttributeValueClassNameFilter" required="false"> + <p>A regular expression used to filter which session attributes will be + replicated. An attribute will only be replicated if the implementation + class name of the value matches this pattern. If the pattern is zero + length or <code>null</code>, all attributes are eligible for + replication. The pattern is anchored so the fully qualified class name + must fully match the pattern. If not specified, the default value of + <code>null</code> will be used.</p> + </attribute> <attribute name="stateTimestampDrop" required="false"> When this node sends a <code>GET_ALL_SESSIONS</code> message to other node, all session messages that are received as a response are queued. @@ -195,6 +204,14 @@ If set to <code>false</code>, all queued session messages are handled. Default is <code>true</code>. </attribute> + <attribute name="warnOnSessionAttributeFilterFailure" required="false"> + <p>If <strong>sessionAttributeNameFilter</strong> or + <strong>sessionAttributeValueClassNameFilter</strong> blocks an + attribute, should this be logged at <code>WARN</code> level? If + <code>WARN</code> level logging is disabled then it will be logged at + <code>DEBUG</code>. The default value of this attribute is + <code>false</code>.</p> + </attribute> </attributes> </subsection> <subsection name="org.apache.catalina.ha.session.BackupManager Attributes"> @@ -218,13 +235,41 @@ another map. Default value is <code>15000</code> milliseconds. </attribute> + <attribute name="sessionAttributeNameFilter" required="false"> + <p>A regular expression used to filter which session attributes will be + replicated. An attribute will only be replicated if its name matches + this pattern. If the pattern is zero length or <code>null</code>, all + attributes are eligible for replication. The pattern is anchored so the + session attribute name must fully match the pattern. As an example, the + value <code>(userName|sessionHistory)</code> will only replicate the + two session attributes named <code>userName</code> and + <code>sessionHistory</code>. If not specified, the default value of + <code>null</code> will be used.</p> + </attribute> + <attribute name="sessionAttributeValueClassNameFilter" required="false"> + <p>A regular expression used to filter which session attributes will be + replicated. An attribute will only be replicated if the implementation + class name of the value matches this pattern. If the pattern is zero + length or <code>null</code>, all attributes are eligible for + replication. The pattern is anchored so the fully qualified class name + must fully match the pattern. If not specified, the default value of + <code>null</code> will be used.</p> + </attribute> <attribute name="terminateOnStartFailure" required="false"> Set to true if you wish to terminate replication map when replication map fails to start. If replication map is terminated, associated context will fail to start. If you set this attribute to false, replication map does not end. It will try to join the map membership in the heartbeat. Default value is <code>false</code> . </attribute> + <attribute name="warnOnSessionAttributeFilterFailure" required="false"> + <p>If <strong>sessionAttributeNameFilter</strong> or + <strong>sessionAttributeValueClassNameFilter</strong> blocks an + attribute, should this be logged at <code>WARN</code> level? If + <code>WARN</code> level logging is disabled then it will be logged at + <code>DEBUG</code>. The default value of this attribute is + <code>false</code>.</p> + </attribute> </attributes> </subsection> </section>
webapps/docs/config/manager.xml+43 −4 modified@@ -167,15 +167,34 @@ </attribute> <attribute name="sessionAttributeNameFilter" required="false"> - A regular expression used to filter which session attributes will be + <p>A regular expression used to filter which session attributes will be distributed. An attribute will only be distributed if its name matches this pattern. If the pattern is zero length or <code>null</code>, all attributes are eligible for distribution. The pattern is anchored so the session attribute name must fully match the pattern. As an example, the value <code>(userName|sessionHistory)</code> will only distribute the two session attributes named <code>userName</code> and <code>sessionHistory</code>. If not specified, the default value of - <code>null</code> will be used. + <code>null</code> will be used.</p> + </attribute> + + <attribute name="sessionAttributeValueClassNameFilter" required="false"> + <p>A regular expression used to filter which session attributes will be + distributed. An attribute will only be distributed if the implementation + class name of the value matches this pattern. If the pattern is zero + length or <code>null</code>, all attributes are eligible for + distribution. The pattern is anchored so the fully qualified class name + must fully match the pattern. If not specified, the default value of + <code>null</code> will be used.</p> + </attribute> + + <attribute name="warnOnSessionAttributeFilterFailure" required="false"> + <p>If <strong>sessionAttributeNameFilter</strong> or + <strong>sessionAttributeValueClassNameFilter</strong> blocks an + attribute, should this be logged at <code>WARN</code> level? If + <code>WARN</code> level logging is disabled then it will be logged at + <code>DEBUG</code>. The default value of this attribute is + <code>false</code>.</p> </attribute> </attributes> @@ -266,16 +285,36 @@ <code>org.apache.catalina.session.StandardManager</code> class. </p> </attribute> + <attribute name="sessionAttributeNameFilter" required="false"> - A regular expression used to filter which session attributes will be + <p>A regular expression used to filter which session attributes will be distributed. An attribute will only be distributed if its name matches this pattern. If the pattern is zero length or <code>null</code>, all attributes are eligible for distribution. The pattern is anchored so the session attribute name must fully match the pattern. As an example, the value <code>(userName|sessionHistory)</code> will only distribute the two session attributes named <code>userName</code> and <code>sessionHistory</code>. If not specified, the default value of - <code>null</code> will be used. + <code>null</code> will be used.</p> + </attribute> + + <attribute name="sessionAttributeValueClassNameFilter" required="false"> + <p>A regular expression used to filter which session attributes will be + distributed. An attribute will only be distributed if the implementation + class name of the value matches this pattern. If the pattern is zero + length or <code>null</code>, all attributes are eligible for + distribution. The pattern is anchored so the fully qualified class name + must fully match the pattern. If not specified, the default value of + <code>null</code> will be used.</p> + </attribute> + + <attribute name="warnOnSessionAttributeFilterFailure" required="false"> + <p>If <strong>sessionAttributeNameFilter</strong> or + <strong>sessionAttributeValueClassNameFilter</strong> blocks an + attribute, should this be logged at <code>WARN</code> level? If + <code>WARN</code> level logging is disabled then it will be logged at + <code>DEBUG</code>. The default value of this attribute is + <code>false</code>.</p> </attribute> </attributes>
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
71- tomcat.apache.org/security-6.htmlnvdVendor AdvisoryWEB
- tomcat.apache.org/security-7.htmlnvdVendor AdvisoryWEB
- tomcat.apache.org/security-8.htmlnvdVendor AdvisoryWEB
- tomcat.apache.org/security-9.htmlnvdVendor AdvisoryWEB
- github.com/advisories/GHSA-mv42-px54-87jwghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2016-0714ghsaADVISORY
- lists.opensuse.org/opensuse-security-announce/2016-03/msg00047.htmlnvdWEB
- lists.opensuse.org/opensuse-security-announce/2016-03/msg00069.htmlnvdWEB
- lists.opensuse.org/opensuse-security-announce/2016-03/msg00082.htmlnvdWEB
- lists.opensuse.org/opensuse-security-announce/2016-03/msg00085.htmlnvdWEB
- marc.infonvdWEB
- rhn.redhat.com/errata/RHSA-2016-1089.htmlnvdWEB
- rhn.redhat.com/errata/RHSA-2016-2045.htmlnvdWEB
- rhn.redhat.com/errata/RHSA-2016-2599.htmlnvdWEB
- rhn.redhat.com/errata/RHSA-2016-2807.htmlnvdWEB
- rhn.redhat.com/errata/RHSA-2016-2808.htmlnvdWEB
- seclists.org/bugtraq/2016/Feb/145nvdWEB
- svn.apache.org/viewvcnvdWEB
- svn.apache.org/viewvcnvdWEB
- svn.apache.org/viewvcnvdWEB
- svn.apache.org/viewvcnvdWEB
- svn.apache.org/viewvcnvdWEB
- svn.apache.org/viewvcnvdWEB
- svn.apache.org/viewvcnvdWEB
- svn.apache.org/viewvcnvdWEB
- www.debian.org/security/2016/dsa-3530nvdWEB
- www.debian.org/security/2016/dsa-3552nvdWEB
- www.debian.org/security/2016/dsa-3609nvdWEB
- www.oracle.com/technetwork/security-advisory/cpujul2018-4258247.htmlnvdWEB
- www.oracle.com/technetwork/security-advisory/cpuoct2016-2881722.htmlnvdWEB
- www.oracle.com/technetwork/security-advisory/cpuoct2017-3236626.htmlnvdWEB
- www.oracle.com/technetwork/topics/security/bulletinapr2016-2952098.htmlnvdWEB
- www.oracle.com/technetwork/topics/security/linuxbulletinoct2016-3090545.htmlnvdWEB
- www.ubuntu.com/usn/USN-3024-1nvdWEB
- access.redhat.com/errata/RHSA-2016:1087nvdWEB
- access.redhat.com/errata/RHSA-2016:1088nvdWEB
- bto.bluecoat.com/security-advisory/sa118nvdWEB
- github.com/apache/tomcat/commit/50f1b1da794cd93b70ab5456d3c2c984408e1506ghsaWEB
- github.com/apache/tomcat/commit/79e8ad03404c131009811855f9a30d8d01c0c736ghsaWEB
- github.com/apache/tomcat/commit/824eb1d1ad922e7652ecf51adb2b9eebb5bb88b5ghsaWEB
- github.com/apache/tomcat/commit/e1b1002129fea4033329f6f619ba219527bbbd40ghsaWEB
- github.com/apache/tomcat/commit/f626da75fd59da82b14dee7b8cc46ad51eefdbe5ghsaWEB
- github.com/apache/tomcat/commit/ff1b659dc366a2ad47cd8f7e3544c796a1b15e46ghsaWEB
- github.com/apache/tomcat80/commit/2e5cc28052e84ba45196949ba602484221bbf33cghsaWEB
- github.com/apache/tomcat80/commit/5430f30c79383e4d2d87785468905fcb00bace58ghsaWEB
- h20566.www2.hpe.com/portal/site/hpsc/public/kb/docDisplaynvdWEB
- h20566.www2.hpe.com/portal/site/hpsc/public/kb/docDisplaynvdWEB
- h20566.www2.hpe.com/portal/site/hpsc/public/kb/docDisplaynvdWEB
- lists.apache.org/thread.html/37220405a377c0182d2afdbc36461c4783b2930fbeae3a17f1333113%40%3Cdev.tomcat.apache.org%3EnvdWEB
- lists.apache.org/thread.html/37220405a377c0182d2afdbc36461c4783b2930fbeae3a17f1333113@%3Cdev.tomcat.apache.org%3EghsaWEB
- lists.apache.org/thread.html/39ae1f0bd5867c15755a6f959b271ade1aea04ccdc3b2e639dcd903b%40%3Cdev.tomcat.apache.org%3EnvdWEB
- lists.apache.org/thread.html/39ae1f0bd5867c15755a6f959b271ade1aea04ccdc3b2e639dcd903b@%3Cdev.tomcat.apache.org%3EghsaWEB
- lists.apache.org/thread.html/b84ad1258a89de5c9c853c7f2d3ad77e5b8b2930be9e132d5cef6b95%40%3Cdev.tomcat.apache.org%3EnvdWEB
- lists.apache.org/thread.html/b84ad1258a89de5c9c853c7f2d3ad77e5b8b2930be9e132d5cef6b95@%3Cdev.tomcat.apache.org%3EghsaWEB
- lists.apache.org/thread.html/b8a1bf18155b552dcf9a928ba808cbadad84c236d85eab3033662cfb%40%3Cdev.tomcat.apache.org%3EnvdWEB
- lists.apache.org/thread.html/b8a1bf18155b552dcf9a928ba808cbadad84c236d85eab3033662cfb@%3Cdev.tomcat.apache.org%3EghsaWEB
- lists.apache.org/thread.html/r03c597a64de790ba42c167efacfa23300c3d6c9fe589ab87fe02859c%40%3Cdev.tomcat.apache.org%3EnvdWEB
- lists.apache.org/thread.html/r03c597a64de790ba42c167efacfa23300c3d6c9fe589ab87fe02859c@%3Cdev.tomcat.apache.org%3EghsaWEB
- lists.apache.org/thread.html/r587e50b86c1a96ee301f751d50294072d142fd6dc08a8987ae9f3a9b%40%3Cdev.tomcat.apache.org%3EnvdWEB
- lists.apache.org/thread.html/r587e50b86c1a96ee301f751d50294072d142fd6dc08a8987ae9f3a9b@%3Cdev.tomcat.apache.org%3EghsaWEB
- lists.apache.org/thread.html/r9136ff5b13e4f1941360b5a309efee2c114a14855578c3a2cbe5d19c%40%3Cdev.tomcat.apache.org%3EnvdWEB
- lists.apache.org/thread.html/r9136ff5b13e4f1941360b5a309efee2c114a14855578c3a2cbe5d19c@%3Cdev.tomcat.apache.org%3EghsaWEB
- security.gentoo.org/glsa/201705-09nvdWEB
- security.netapp.com/advisory/ntap-20180531-0001ghsaWEB
- web.archive.org/web/20170204045529/http://www.securityfocus.com/bid/83327ghsaWEB
- web.archive.org/web/20170601064840/http://www.securitytracker.com/id/1035069ghsaWEB
- web.archive.org/web/20170927131230/http://www.securitytracker.com/id/1037640ghsaWEB
- www.securityfocus.com/bid/83327nvd
- www.securitytracker.com/id/1035069nvd
- www.securitytracker.com/id/1037640nvd
- security.netapp.com/advisory/ntap-20180531-0001/nvd
News mentions
0No linked articles in our index yet.