Tornado Authentication and "Best Practices"

1,108 views
Skip to first unread message

Michael Wilson

unread,
Nov 8, 2012, 10:19:45 PM11/8/12
to python-...@googlegroups.com
So I was perusing: 


(Normally, I take these sorts of pages with a grain of salt, but in this case the contributors are particularly high grade)

In any case, I was reading this bit:

PART II: How To Remain Logged In - The Infamous "Remember Me" Checkbox


2. And DO NOT STORE THE PERSISTENT LOGIN COOKIE (TOKEN) IN YOUR DATABASE, ONLY A HASH OF IT! The login token is Password Equivalent, so if an attacker got his hands on your database, he could use the tokens to log in to any account, just as if they were cleartext login-password combinations. Therefore, use strong salted hashing (bcrypt / phpass) when storing persistent login tokens.


Right now, I'm following the examples verbatim and using set_secure_cookie/get_secure_cookie to store an user identifier to indicate the user's logged in, and it of course (again probably foolishly) doubles as a "keep me logged in" device.

After reading this, I suspect that's completely wrong, and should be storing something else in the cookie, but to be useful that would be a decryptable version of the user identifier, which would seem to have it's own insecurities. I would guess a server side secret would solve that, but I'm not sure that - in the end - is more secure than what's in place with _xsrf and set/get_secure_cookie.

Does anyone here have any experience with this and tornado that would like to share?

Thanks! 



Ben Darnell

unread,
Nov 10, 2012, 11:12:20 PM11/10/12
to Tornado Mailing List
I think the author's argument against persistent login is misplaced; persistent login cookies are compatible with the security needs of most sites.  However, the points he make here are valid and are best practices be followed whether your login cookies are persistent or not.  (Tornado's example apps don't do this.  I'd like to do a better job of steering people towards best practices, but I also don't want to overwhelm the simple examples with complex login functionality).  

Specifically, you should store a random session key for each user in your database and include this session key in your signed cookies.  The session key would be deleted or reset to a new random value when the user explicitly logs out or changes their password, and cookies would be rejected if they didn't have the right session key.

Storing only a hash of the session key in the database is a good idea, although this means that you have to have a one-to-many relationship between users and session keys (unless you want a login on one browser to invalidate any current logins on other browsers), since you won't be able to reuse the previous session key the next time the user logs in.  The recommendation to use bcrypt or equivalent is kind of silly, though - a sufficiently large random session key cannot be brute forced even if the hash is a fast one, so just use your preferred SHA-family hash (bcrypt and friends are for when you're using low-entropy keys so that humans can remember them).  Also note that it's important to use a good cryptographic random number generator (e.g. os.urandom instead of anything from the python stdlib's random module).  

Server-side session keys ensure that even if a cookie is stolen (which can happen for non-persistent cookies too) it can be invalidated as soon as the user logs out, instead of being usable until it expires.  Hashing the session keys in the database means that even an attacker who has read-only access to your database and a copy of your cookie-signing key cannot forge login cookies for arbitrary users.  

-Ben

Joe Bowman

unread,
Nov 12, 2012, 10:26:30 AM11/12/12
to python-...@googlegroups.com, b...@bendarnell.com
I've taken this process a bit further with some session libraries I've written. I've made the session key valid for x amount of time (usually seconds). When present with an expired key it would automatically generate a new one and remove the old one.

This did cause some problems with ajax heavy sites, so I would keep a few keys back, say 2 or 3. This allowed simultaneous requests to not fail and the browser would still keep state as it always had just 1 session identifier in it's cookies regardless of the order of requests processed. 

The nice thing about this is even if the session was hijacked by someone on the same network, the hijacker and the user being online at the same time would eventually cause the session identifiers to diverge and one of them would be logged out. 

It's a bit complicated, some people say overly so. But I found it useful, especially on google appengine when ssl for hosted domains wasn't an option. Not sure if appengine ever made that possible.

If you can go totally https for logged in connections then I believe that would reduce the risk that such a complex system wouldn't be as necessary.
Reply all
Reply to author
Forward
0 new messages