Migrate to Ehcache 3 with hash-flooding DoS protection#277
Conversation
Co-authored-by: hazendaz <975267+hazendaz@users.noreply.github.com>
Co-authored-by: hazendaz <975267+hazendaz@users.noreply.github.com>
| public int getSize() { | ||
| return cache.getSize(); | ||
| int count = 0; | ||
| for (org.ehcache.Cache.Entry<Object, Object> ignored : getOrCreateCache()) { |
Check notice
Code scanning / CodeQL
Unread local variable Note
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 14 days ago
In general, to fix an unread local variable in a loop, either remove the variable if possible or restructure the loop so that iteration does not require a throwaway variable. When using an enhanced for-loop, Java requires a loop variable, so avoiding an unused variable means not using the enhanced for-loop in cases where the element is not needed.
Here, the method getSize() only needs to count the number of entries in the Ehcache; it never uses the Entry objects themselves. The best fix without changing functionality is to replace the enhanced for-loop with an explicit Iterator over the cache, using hasNext() to drive the loop. This avoids declaring an unused variable while preserving the existing counting behavior.
Concretely, in src/main/java/org/mybatis/caches/ehcache/AbstractEhcacheCache.java, lines 143–148 should be updated so that:
- The enhanced for-loop
for (org.ehcache.Cache.Entry<Object, Object> ignored : getOrCreateCache()) { ... }is replaced with creation of ajava.util.Iterator<?>fromgetOrCreateCache().iterator(). - A
while (iterator.hasNext()) { ... }loop incrementscountonce per element without ever binding the element to a local variable. - No new imports are required because we can refer to
java.util.Iteratorby its fully qualified name.
This keeps the method semantics identical (iterating over all entries and counting them) while eliminating the unread local variable.
| @@ -142,7 +142,9 @@ | ||
| @Override | ||
| public int getSize() { | ||
| int count = 0; | ||
| for (org.ehcache.Cache.Entry<Object, Object> ignored : getOrCreateCache()) { | ||
| java.util.Iterator<?> iterator = getOrCreateCache().iterator(); | ||
| while (iterator.hasNext()) { | ||
| iterator.next(); | ||
| count++; | ||
| } | ||
| return count; |
Co-authored-by: hazendaz <975267+hazendaz@users.noreply.github.com>
| * @see <a href="https://github.com/jhipster/generator-jhipster/issues/28546">jhipster/generator-jhipster #28546</a> | ||
| * @see <a href="https://github.com/mybatis/ehcache-cache/issues/61">mybatis/ehcache-cache #61</a> | ||
| */ | ||
| final class HashKeyWrapper implements Serializable { |
There was a problem hiding this comment.
if you restrict the minimum to v3.11.1 then this should be resolved for you.
There was a problem hiding this comment.
Thanks @ben-manes, since users can override, I presume its otherwise fine to still be present regardless?
Replaces the EOL
net.sf.ehcache:ehcache:2.10.9.2dependency withorg.ehcache:ehcache:3.10.8and addresses the hash-flooding DoS vulnerability inherent in Ehcache 3's use of rawhashCode()in its internal hash structures.Dependency change
net.sf.ehcache:ehcache:2.10.9.2→org.ehcache:ehcache:3.10.8jaxb-runtime(only needed for XML-based Ehcache config; its transitive deps resolve from blocked HTTP repositories)API migration (
AbstractEhcacheCache,EhcacheCache,EhBlockingCache)setTimeToLiveSeconds,setMaxEntriesLocalHeap, etc.) to be applied beforehand; config changes after first use atomically recreate the cacheNullValuesentinel, transparently unwrapped on readgetSize()via iteration (Ehcache 3'sCachehas nosize()method)EhBlockingCache.removeObjectnow callssuper.removeObject()instead ofcache.put(key, null); Ehcache 3 has noBlockingCacheequivalent so blocking must be provided by the caller (e.g. MyBatis'sBlockingCachedecorator)Hash-flooding DoS protection (
HashKeyWrapper)Ehcache 3 uses raw
key.hashCode()in its internal hash structures with no spreading. On a bounded cache an attacker who controls query parameters (which feed MyBatis'sCacheKey) can craft keys that all collide in the same bucket, degrading every operation from O(1) to O(n).HashKeyWrapperwraps every key and applies the Murmur3 fmix32 finalizer before the value reaches Ehcache:HashKeyWrapperisSerializable, handlesnullkeys, and is transparent to callers —equals()delegates to the wrapped key.ObjectSerializerNew
Serializer<Object>backed by standard Java serialization, available for configurations that add off-heap or disk tiers.✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.