[jboss-user] [Tomcat, HTTPD, Servlets & JSP] - Re: Session is getting merge with other session

bstansberry@jboss.com do-not-reply at jboss.com
Thu Dec 20 21:09:47 EST 2007


If at the end of your application workflow, there is a natural place to do session.invalidate(), then sure, I encourage you to do that. That will save you memory.

Session ids are generated from a VM singleton, using a random generator seeded by a timestamp and the VM's free memory (which are unlikely to be duplicated across VMs in a cluster).  The class follows, in case you are interested.  I've never heard a case of this algorithm generating noticeable duplicate ids. I have seen reports on this forum of  non-clustered webapps getting duplicate ids when more than one JBoss instance is on the same machine.  But clustered session ids use a different algorithm.

Have you heard of more than 1 case of this?  If you have, that would in my mind pretty much rule out a chance session id collision.

Anyway, invalidating the session if you know its done would reduce the already extremely low chance of a problem from session id collision.

Here's the SessionIDGenerator class:


  | /*
  |  * JBoss, Home of Professional Open Source.
  |  * Copyright 2006, Red Hat Middleware LLC, and individual contributors
  |  * as indicated by the @author tags. See the copyright.txt file in the
  |  * distribution for a full listing of individual contributors.
  |  *
  |  * This is free software; you can redistribute it and/or modify it
  |  * under the terms of the GNU Lesser General Public License as
  |  * published by the Free Software Foundation; either version 2.1 of
  |  * the License, or (at your option) any later version.
  |  *
  |  * This software is distributed in the hope that it will be useful,
  |  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  |  * Lesser General Public License for more details.
  |  *
  |  * You should have received a copy of the GNU Lesser General Public
  |  * License along with this software; if not, write to the Free
  |  * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
  |  * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
  |  */
  | package org.jboss.web.tomcat.service.session;
  | 
  | import java.util.Collection;
  | import java.util.HashSet;
  | import java.util.Iterator;
  | import java.util.Random;
  | import java.security.MessageDigest;
  | import java.security.NoSuchAlgorithmException;
  | import java.security.SecureRandom;
  | 
  | import org.jboss.logging.Logger;
  | 
  | /**
  |  * Unique session id generator
  |  *
  |  * @author Ben Wang
  |  */
  | public class SessionIDGenerator
  | {
  |    protected final static int SESSION_ID_BYTES = 16; // We want 16 Bytes for the session-id
  |    protected final static String SESSION_ID_HASH_ALGORITHM = "MD5";
  |    protected final static String SESSION_ID_RANDOM_ALGORITHM = "SHA1PRNG";
  |    protected final static String SESSION_ID_RANDOM_ALGORITHM_ALT = "IBMSecureRandom";
  |    protected Logger log = Logger.getLogger(SessionIDGenerator.class);
  | 
  |    protected MessageDigest digest = null;
  |    protected Random random = null;
  |    protected static final SessionIDGenerator s_ = new SessionIDGenerator();
  |    
  |    protected String sessionIdAlphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-*";
  | 
  |    public static SessionIDGenerator getInstance()
  |    {
  |       return s_;
  |    }
  | 
  |    /**
  |     * The SessionIdAlphabet is the set of characters used to create a session Id
  |     */
  |    public void setSessionIdAlphabet(String sessionIdAlphabet) 
  |    {
  |       if (sessionIdAlphabet.length() != 65) {
  |          throw new IllegalArgumentException("SessionIdAlphabet must be exactly 65 characters long");
  |       }
  | 
  |       checkDuplicateChars(sessionIdAlphabet);
  | 
  |       this.sessionIdAlphabet = sessionIdAlphabet;
  |    }
  | 
  |    protected void checkDuplicateChars(String sessionIdAlphabet) {
  |       char[] alphabet = sessionIdAlphabet.toCharArray();
  |       for (int i=0; i < alphabet.length; i++) {
  |           if (!uniqueChar(alphabet, sessionIdAlphabet)) {
  |               throw new IllegalArgumentException("All chars in SessionIdAlphabet must be unique");
  |           }
  |       }
  |    }
  |       
  |    // does a character appear in the String once and only once?
  |    protected boolean uniqueChar(char c, String s) {
  |        int firstIndex = s.indexOf(c);
  |        if (firstIndex == -1) return false;
  |        return s.indexOf(c, firstIndex + 1) == -1;
  |    }
  | 
  |    /**
  |     * The SessionIdAlphabet is the set of characters used to create a session Id
  |     */
  |    public String getSessionIdAlphabet() {
  |       return this.sessionIdAlphabet;
  |    }
  |    
  |    public synchronized String getSessionId()
  |    {
  |       String id = generateSessionId();
  |       if (log.isDebugEnabled())
  |          log.debug("getSessionId called: " + id);
  |       return id;
  |    }
  | 
  | 
  |    /**
  |     * Generate a session-id that is not guessable
  |     *
  |     * @return generated session-id
  |     */
  |    protected synchronized String generateSessionId()
  |    {
  |       if (this.digest == null)
  |       {
  |          this.digest = getDigest();
  |       }
  | 
  |       if (this.random == null)
  |       {
  |          this.random = getRandom();
  |       }
  | 
  |       byte[] bytes = new byte[SESSION_ID_BYTES];
  | 
  |       // get random bytes
  |       this.random.nextBytes(bytes);
  | 
  |       // Hash the random bytes
  |       bytes = this.digest.digest(bytes);
  | 
  |       // Render the result as a String of hexadecimal digits
  |       return encode(bytes);
  |    }
  | 
  |    /**
  |     * Encode the bytes into a String with a slightly modified Base64-algorithm
  |     * This code was written by Kevin Kelley <kelley at ruralnet.net>
  |     * and adapted by Thomas Peuss <jboss at peuss.de>
  |     *
  |     * @param data The bytes you want to encode
  |     * @return the encoded String
  |     */
  |    protected String encode(byte[] data)
  |    {
  |       char[] out = new char[((data.length + 2) / 3) * 4];
  |       char[] alphabet = this.sessionIdAlphabet.toCharArray();
  | 
  |       //
  |       // 3 bytes encode to 4 chars.  Output is always an even
  |       // multiple of 4 characters.
  |       //
  |       for (int i = 0, index = 0; i < data.length; i += 3, index += 4)
  |       {
  |          boolean quad = false;
  |          boolean trip = false;
  | 
  |          int val = (0xFF & (int) data);
  |          val <<= 8;
  |          if ((i + 1) < data.length)
  |          {
  |             val |= (0xFF & (int) data[i + 1]);
  |             trip = true;
  |          }
  |          val <<= 8;
  |          if ((i + 2) < data.length)
  |          {
  |             val |= (0xFF & (int) data[i + 2]);
  |             quad = true;
  |          }
  |          out[index + 3] = alphabet[(quad ? (val & 0x3F) : 64)];
  |          val >>= 6;
  |          out[index + 2] = alphabet[(trip ? (val & 0x3F) : 64)];
  |          val >>= 6;
  |          out[index + 1] = alphabet[val & 0x3F];
  |          val >>= 6;
  |          out[index + 0] = alphabet[val & 0x3F];
  |       }
  |       return new String(out);
  |    }
  | 
  |    /**
  |     * get a random-number generator
  |     *
  |     * @return a random-number generator
  |     */
  |    protected synchronized Random getRandom()
  |    {
  |       long seed;
  |       Random random = null;
  | 
  |       // Mix up the seed a bit
  |       seed = System.currentTimeMillis();
  |       seed ^= Runtime.getRuntime().freeMemory();
  | 
  |       try
  |       {
  |          random = SecureRandom.getInstance(SESSION_ID_RANDOM_ALGORITHM);
  |       }
  |       catch (NoSuchAlgorithmException e)
  |       {
  |          try
  |          {
  |             random = SecureRandom.getInstance(SESSION_ID_RANDOM_ALGORITHM_ALT);
  |          }
  |          catch (NoSuchAlgorithmException e_alt)
  |          {
  |             log.error("Could not generate SecureRandom for session-id randomness", e);
  |             log.error("Could not generate SecureRandom for session-id randomness", e_alt);
  |             return null;
  |          }
  |       }
  | 
  |       // set the generated seed for this PRNG
  |       random.setSeed(seed);
  | 
  |       return random;
  |    }
  | 
  |    /**
  |     * get a MessageDigest hash-generator
  |     *
  |     * @return a hash generator
  |     */
  |    protected synchronized MessageDigest getDigest()
  |    {
  |       MessageDigest digest = null;
  | 
  |       try
  |       {
  |          digest = MessageDigest.getInstance(SESSION_ID_HASH_ALGORITHM);
  |       }
  |       catch (NoSuchAlgorithmException e)
  |       {
  |          log.error("Could not generate MessageDigest for session-id hashing", e);
  |          return null;
  |       }
  | 
  |       return digest;
  |    }
  | 
  |    public static void main(String[] args)
  |    {
  |       SessionIDGenerator gen = SessionIDGenerator.getInstance();
  |       
  |       HashSet set = new HashSet();
  |       String id = null;
  |       for (int i = 0; i > -1; i++)
  |       {
  |          id = gen.getSessionId();
  |          
  |          if (set.add(id) == false)
  |          {
  |             System.out.println(i + " FAILED -- duplicate id " + id);
  |             break;
  |          }
  |          
  |          if (i % 1000 == 0 && i > 0)
  |          {
  |             System.out.println("Tested " + i + " session ids");
  |          }
  |          if (i % 50000 == 0 && set.size() > 450000)
  |          {
  |             System.out.println("Discarding...");
  |             int k = 0;
  |             int discarded = 0;
  |             for (Iterator it = set.iterator(); it.hasNext() && discarded < 50000; k++)
  |             {
  |                it.next();
  |                if (k % 10 == 0) 
  |                {
  |                   it.remove();
  |                   discarded++;
  |                }
  |             }
  |             System.out.println("Discarding completed");
  |          }
  |       }
  |    }
  | }
  | 

View the original post : http://www.jboss.com/index.html?module=bb&op=viewtopic&p=4114862#4114862

Reply to the post : http://www.jboss.com/index.html?module=bb&op=posting&mode=reply&p=4114862



More information about the jboss-user mailing list