The OnlineGlom demo does not require a login. However, the code does let you set up a server that requires a login, and I noticed that a successful login for one person became a login for everybody else. So after the first login, it was as if no login was required for anybody. Yes, really. Of course, this would not do.
So I fixed that, I think, learning some things about Java Servlet sessions along the way. This text is mostly for my own reference, and so that people can tell me how wrong I am, because I’d like to know about that.
In the server-side code
Java servlets already set a JSESSIONID cookie in the browser, but you shouldn’t try to use that cookie to maintain a login across browser sessions. Instead, I now set and get a custom Cookie, in the server code, using javax.servlet.http.Cookie. HttpSession.getId() conveniently provides a unique-enough session ID for me to use in the Cookie. This page about Cookies with GWT seems to suggest setting the cookie in the client-side JavaScript code, using com.google.gwt.user.client.Cookies, but that sounds rather wrong.
I now store the username and password (Yes, that’s not good, so keep reading), associated with the session ID, in a structure that’s associated with the ServletContext, via the javax.servlet.ServletContext.setAttribute() method. I get the ServletContext via the ServletConfig.getServletContext() method. I believe that this single instance is available to the entire web “app”, and it seems to work across my various servlets. For instance, if I login to view a regular page, the images servlet can also then provide images to show in the page. I’d really like to know if this is not the right thing to do.
However, it still stores your PostgreSQL username and password in memory, so it can use it again if you have the cookie from your last successful login. It does not store the password on disk, but that is still not good, because it could presumably still allow someone to steal all the passwords after a breakin, which would then endanger users who use the same password on other website. I cannot easily avoid this because it’s the PostgreSQL username and password that I’m using for login. PostgreSQL does store a hash rather than the plaintext password, but still requires the plaintext password to be supplied to it. I think I’ll have to generate PostgreSQL passwords and hide them behind a separate login username/password. Those passwords will still be stored in plaintext, but we won’t be storing the password entered by the user. I’d like to make this generic enough that I can use other authentication systems, such as Google’s for App Engine.
To avoid session hijacking, I made the cookie “secure”, meaning that it may only be provided via a secure protocol, such as HTTP. I believe this also means that client (javascript) code is not allowed to read it, so it can only be read by the server via HTTP(S). I did that with the javax.servlet.http.Cookie.setSecure() method, though I had to make a build change to make that available.
The login servlet now checks that it has been called via HTTPS, by using the ServletRequest.isSecure() method, and uses HTTPS when testing via mvn gwt:run. It refuses to do any authentication if HTTPS was not used, logging an error on the server.
In the client side code
I added a check for what protocol is used. If it’s not https then I warn that login cannot work. This does not add any security, but it’s a helpful hint.
Actually, the entire site must therefore be served via HTTPS, not just the login page, or we would violate the Same Origin Policy by mixing protocols, which the browser would rightfully complain about. At this point I noticed that most serious sites with logins now use HTTPS for their entire site. For instance, Google, Amazon, Facebook. This seems like a good simple rule, though I wonder if many projects don’t enforce it just to make debugging easier.
I also converted the popup login dialog into a proper login page, making sure that it takes the user to the desired page afterwards.
First of all it’s good that Online Glom gains an authentication strategy. From your explanations I understand that you are using the ServletContext to store the users credentials. I guess you are using a Map for that, right? (Storing a single cookie does not work because the ServletContext is the same for all users). Storage like this has one major downside: Server restarts and redeployments terminate your session. Tomcat, for example provides, a database session store so that session (i.e. cookies) survive restarts (org.apache.catalina.session.PersistentManager).
Therefore I’d suggest to go a similar route. But this still does not fix the security flaw of storing username/password, but also put’s the data from memory into a database (bad thing TM). But currently I don’t see any useful workaround for that :(
Thanks. I didn’t know about that.
Murray,
I’m moving my site, RunPartner, to GWT currently and did something similar. However, I found it simpler to use Spring Security and simply route my authentication popup through the handlers for Spring while securing protect assets/methods with annotations and protected url contexts. This has built in features for protection from Session Fixation Attacks, “remember me” cookies, and also allows preconfigured persisting of cookie information to a database table to allow for session recovery when a server is restarted, or a user moves to another edge node.
Even if you don’t use Spring Security, their classes are a great starting point if you plan to role your own implementation. One such example is that SS drops the user login credentials the moment the authentication check is complete. No reason to keep those in memory. It also allows you to easily supply a custom salting/hashing algorithm with is critical for any storage of user credentials.
On the facet of running an entire application from HTTPS, it is a nice to have feature, but not necessary. If you’re in say finance, certainly SSL the entire session. If you’re Gmail in China where the state is censoring you, SSL the entire session. For best practices however, just login/account update needs SSL. As long as an account cannot be compromised with a hijacked session (i.e. they cannot update email/credentials/close account), the hijacker’s ability to wreak havoc is limited. What’s the threat of hijacking verses the loses to your site WRT the overhead of SSL’ing everything including updating your hosting (i.e. need certs for subdomains, custom IP for each virtual host, debugging pain etc.)
Yes, I’ve been meaning to take a proper look at Spring Security. I’d rather not depend on my own custom code.
Dear Murray,
First of all for normal circumstances, you must not use cookies in Java servlets to maintain the session.
A user of a web application is normally an object that should be stored in the HttpSession object (request.getSession()). Using the servlet context to maintain user sessions is totally wrong since it is shared to all sessions. (spring security that joseph mentioned does this automatically for you)
Now depending on the application server you use there are 3 ways to store the sessions (database, filesystem, memory). And this is a general configuration and you don’t need to write any special code to persist the session.
As for the security of the sessions, there are several techniques to avoid all the kinds of attacks and I think there is already an api that does this automatically.
I hope this helps
Thanks for the good advice. Clearly I need to just look properly at Spring Security.
I thought that the HttpSession was only for the lifetime of the browser session. so it would be discarded when the browser window is closed. Am I wrong?
Note that session identifiers need to be securely generated random data, not something that is probably predictable. Otherwise one user can simply use another users identifier and pretend to be that user.