I'm writing my own sessions controller that issues a unique id to a user once logged in, and then verifies and authenticates that unique id at every page load. What is the most secure way to generate such an id? Should the unique id be completely random? Is there any downside to including the user id as part of the unique id?
The best way would be to generate a UUID, using the uuid.uuid1
function:
Generate a UUID from a host ID, sequence number, and the current time.
Use the UUID module in 2.5 and higher. This way they can't be guessed since they are not sequential. If you want them to be really secure you could salt them with the user's id and then SHA1 hash them, Base64 encode the result and then use that as the value. That way you could verify on the server side that they really are your cookies and not just something that some spammer is generating and throwing at your server. And to make them even more secure make sure they only last for a set amount of time on the server as well as the client. Python 2.6.5 has an HMAC implemenation built in that you could use on the generated UUID as well.
Go buy Bruce Schneier's Secrets and Lies and his Practical Cryptography. Go ahead and order Ross Anderson's Security Engineering 2nd Ed. while you're at it. Now, read Secrets and Lies -- trust me, it's a fun read. :) Then read Practical Cryptography. At that point you should have a better grounding in what you need to do when implementing software that needs some security. Go ahead and do your rough-draft implementation. Now that your copy of Security Engineering has arrived, read it... though that one you may want to digest a bit slower; it's a rather heavy tome.
There is also a whitepaper on web authentication dos and don'ts that is worth reading and a bit more directly applicable to what you're doing. I still recommend the books above.
And another timely article from LWN.net which lists a good number of pitfalls you need to work to avoid. (BTW, LWN.net is well worth subscribing to, and I highly recommend it. The above link allows free access to that article which otherwise would not be available to non-subscribers yet.)
If the only information in the cookie is an identifier, essentially a label, the only attack you're trying to protect from is an attacker guessing what it is. So use some random bytes. Enough random bytes that it's "unguessable."
Then squish the random bytes into something that fits within the confines of which characters you can use in a HTTP cookie. base64 works for this. It's not absolutely optimal, because there are more than 64 cookie-safe characters and it tends to leave trailing ==
characters which add bytes to the header but not randomness, but that's fine. It works and Python can probably do the base64 encoding faster than anything else we can come up with.
If you also want to prepend the user ID to it, that will make things easier to trace and debug, which will probably make your life easier, so go ahead. It doesn't give an attacker any significant advantage. (unless they manage to steal one without being able to otherwise determine what user they're stealing from, which seems unlikely.)