diff --git a/modules/transport/http/src/main/java/org/apache/axis2/transport/http/server/SessionManager.java b/modules/transport/http/src/main/java/org/apache/axis2/transport/http/server/SessionManager.java index c3e6f2ce84..e44c3b6328 100644 --- a/modules/transport/http/src/main/java/org/apache/axis2/transport/http/server/SessionManager.java +++ b/modules/transport/http/src/main/java/org/apache/axis2/transport/http/server/SessionManager.java @@ -44,7 +44,7 @@ public synchronized SessionContext getSessionContext(String sessionKey) { sessionContext = (SessionContext) this.sessionmap.get(sessionKey); } if (sessionContext == null) { - sessionKey = UIDGenerator.generateUID(); + sessionKey = UIDGenerator.generateURNString(); sessionContext = new SessionContext(null); sessionContext.setCookieID(sessionKey); this.sessionmap.put(sessionKey, sessionContext); diff --git a/modules/transport/http/src/test/java/org/apache/axis2/transport/server/SessionManagerTest.java b/modules/transport/http/src/test/java/org/apache/axis2/transport/server/SessionManagerTest.java index 16b86eba19..a98d2b94d5 100644 --- a/modules/transport/http/src/test/java/org/apache/axis2/transport/server/SessionManagerTest.java +++ b/modules/transport/http/src/test/java/org/apache/axis2/transport/server/SessionManagerTest.java @@ -72,4 +72,42 @@ public void testGetSessionContext() { assertFalse(sessionKey.equals(ctx3.getCookieID())); } + /** + * The session key is the only credential a caller needs to be bound to a + * SessionContext, so the next one must not follow from the last. A key built + * from a per-thread counter differs from its predecessor only by the low bits + * that change on increment, which leaves at most 64 candidates. + */ + public void testSessionKeyNotPredictableFromPreviousKey() { + for (int i = 0; i < 100; i++) { + String previous = manager.getSessionContext(null).getCookieID(); + String next = manager.getSessionContext(null).getCookieID(); + for (int t = 0; t < 64; t++) { + long mask = t == 63 ? -1L : (1L << (t + 1)) - 1; + assertFalse(next.equals(flipCounterBits(previous, mask))); + } + } + } + + // Applies mask to the reversed-hex counter that leads a counter-based key. + private static String flipCounterBits(String key, long mask) { + if (key.length() < 16) { + return null; + } + long counter = 0; + for (int i = 0; i < 16; i++) { + int nibble = Character.digit(key.charAt(i), 16); + if (nibble < 0) { + return null; + } + counter |= (long) nibble << (4 * i); + } + counter ^= mask; + StringBuilder buffer = new StringBuilder(key.length()); + for (int i = 0; i < 16; i++) { + buffer.append(Character.forDigit((int) (counter >> (4 * i)) & 15, 16)); + } + return buffer.append(key.substring(16)).toString(); + } + }