Yesterday I asked Are GUIDs generated on Windows 2003 safe to use as session IDs? and the answer combined with combined with this article GUIDs are globally unique, but substrings of GUIDs aren't prompted me to think about replacing my current mechanism of using GUIDs as session ID's in cookies.
Because it's a bit of work to make that change I decided to run a quick GUID test on my Vista PC to see if a sequence of GUIDs was obviously deterministic (what I'm worried about is if an attacker was able to get a sequence of GUIDs generated by my server they would be able to generate new matching ones).
In Raymond Chen's article (which references this very old spec UUIDs and GUIDs from 1998) the GUID is made up of:
- 60 bits of timestamp,
- 48 bits of computer identifier,
- 14 bits of uniquifier, and
- six bits are fixed
Going by that if I generate 10 GUIDs the first 15 ASCII chars (excluding '-') are the timestamp, the next 12 ASCII chars are the computer identifier, the next 3.5 ASCII characters are random and the last 1.5 characters are fixed.
Getting 10 GUIDs on my Vista PC using the .Net System.Guid.NewGuid() yields:
b4e95ead-3619-4dc2-9102-cf7ab0efd927
a45ee719-decd-46b2-8355-7becbe406f74
9af68d75-35a0-4907-b6ab-f15e33acfe96
bed88fa3-3209-4a19-97dd-85d5428ea5f4
123cb39b-8d81-41c6-8894-f1257a8f7606
e2b1f6b1-5791-4a18-80a9-5dc668574ecb
c52aa660-2629-4659-bb83-5583081e5a1c
76eda32d-ceda-412e-8ade-30c47416e954
cbc4d45e-7281-40d2-9f90-00539b04fe98
be36524c-267c-4791-bc9e-3c20b29d7615
The only discernible pattern from a quick visual inspection is that the 13th ASCII character is always 4.
I'm again left wondering if relying on System.Guid to generate pseudo random session IDs is strong enough to protect a web application where cracking a session ID would be worth at most thousands of dollars?
Update: Instead of using a GUID I now plan to generate my session ID's using the approach below. I'm converting the 384 bit random number to a 0x00 byte string so that it's suitable for use in an HTTP cookie.
RNGCryptoServiceProvider rngProvider = new RNGCryptoServiceProvider();
byte[] myKey = new byte[48];
rngProvider.GetBytes(myKey);
string sessionID = null;
myKey.ToList().ForEach(b => sessionID += b.ToString("x2"));
Console.WriteLine(sessionID);