|
LoginSecurityFAQ
Explains how to do logins with GWT in a secure fashion.
Type-FAQ This FAQ page is slightly more hands-on, but you should definitely also read Security for GWT applications. Login SecurityThis article describes how to do the following:
Creating a 'login' pageA login page is consists really of just a TextBox for the username or email address or other identifier (henceforth known as username, and a PasswordTextBox for the password. Add a 'login' button to the mix and that's all there's to it. This login button will result in an AJAX call to your server, using either JSON over RequestBuilder or GWT-RPC. Your server will then validate this login, and return a sessionID to your GWT app. The GWT app will store this sessionID in a static field. For every further request your GWT app makes to your server, include this sessionID in the payload of the request. (Either in the JSON data or the object you are transferring using GWT-RPC). NB: Do NOT attempt to use the Cookie header to transfer the sessionID from GWT to the server; it is fraught with security issues that will become clear in the rest of this article. You MUST transfer the sessionID in the payload of the request. For an example of why this can fail, see CrossSiteRequestForgery. Storing user/pass info on the serverDuring account registration (or a change password operation), your user will specify a username and a password that he'd like to use. You'll need to store this information on your server somehow so that you can authenticate future login requests. However, storing the passwords without encryption on your server means that losing your database, or a compromised server, will result in your user's passwords being out on the street. The vast majority of users 're-use' their passwords, which means virtually their entire online existence is at risk. This is irresponsible, not to mention the bad press such a breach would generate. Fortunately there are ways to 'mangle' the password so that you can still authenticate users, but without storing the password itself. This act is called hashing. The basic mathematical principle behind hashing is explained on this wikipedia page, but all you really need to know is: Use BCrypt. It's a very simple algorithm available for a number of platforms that takes a password and converts it into a string which can be used to ascertain that someone knows the password without actually storing the password. It's specifically written for login authentication and security experts have thoroughly reviewed it.
So, whenever a user registers or changes a password, send the password to your server as usual, but then let your server BCrypt the content. Then, when a user logs in, simply ask the BCrypt framework to check if the password entered is validated by the hash that you stored in e.g. a database. Add new account to database example for java using jBCrypt: String password = /*(get password from incoming JSON or GWT-RPC request)*/;
String hash = BCrypt.hashpw(password, BCrypt.genSalt());
//(create new user entry in db storing ONLY username and hash, *NOT* the password).Check if an incoming user/pass combo is valid for java using jBCrypt: String password = /*(get password from incoming JSON or GWT-RPC request)*/;
String hashFromDB = /*(obtain hash from user's db entry)*/;
boolean valid = BCrypt.checkpw(password, hashFromDB);
if ( valid ) generateSessionIDAndSendItBackToClient();
else sendErrorToClient("Wrong Username or Password.");BCrypt is similarly trivial to use for the other languages. How to remember loginsOur login system so far misses a useful feature: For now it requires users to log in again every time. We can use Cookies to allow the user's web browser to 'remember' the login. In GWT, to set the cookie (which you'd do right after your GWT code receives the response as we did in the previous code fragment): String sessionID = /*(Get sessionID from server's response to your login request.)*/;
final long DURATION = 1000 * 60 * 60 * 24 * 14; //duration remembering login. 2 weeks in this example.
Date expires = new Date(System.currentTimeMillis() + DURATION);
Cookies.setCookie("sid", sessionID, expires, null, "/", false);Now you can run the following code right after your !EntryPoint begins execution: String sessionID = Cookies.getCookie("sid");
if ( sessionID != null ) checkWithServerIfSessionIdIsStillLegal();
else displayLoginBox();Remember - you must never rely on the sessionID sent to your server in the cookie header ; look only at the sessionID that your GWT app sends explicitly in the payload of messages to your server. auto-complete and GWTCertain browsers offer the option to store username/password combinations so that they are automatically filled in for the user the next time they visit the page. However, usually this works only if there are two input boxes (one of type password), and only if those input boxes are loaded along with the main page, and not added later via javascript (which is what GWT does). In order to force these input boxes to show up, stuff them in the project's HTML in an invisible div. Then, in GWT, instead of creating a TextBox and a PasswordTextBox, make the div visible and read out the values using the DOM. library. TODO: Expand on this section. Using Acegi with GWTAcegi is a security system for Spring, but it can also be used separately. Bruno Marchesson wrote an article about it: Using Acegi with GWT. Extra Security DiscussionBased on a chat with GWT's Bob Vawter See http://groups.google.com/group/Google-Web-Toolkit/browse_thread/thread/59f3aafcf4951523 TODO: Explain how without https security is limited regardless, touch on bobv's proposal for running login outside of GWT over HTTPS, setting a (non-secure) cookie with a key, redirecting to a non-https GWT app which reads the cookie again, and then using a JS version of e.g. bcrypt or some other hasher to generate fresh hash values for every request, involving a counter so that the server knows which hashes have been 'used' - to protect against replay attacks. The highlights of this are discussed here |
Integration with Kerberos type authentication such as CAS or Google Auth? See http://groups.google.com/group/Google-Web-Toolkit/browse_thread/thread/f02b62e5df62e080/1f1e6a1b882f1295?lnk=gst&q=cas#1f1e6a1b882f1295
thank's for those tips.
However, can you give us the generateSessionIDAndSendItBackToClient() function? what do you embed within SessionId? structure? If the server starts again, the sessionIds are lost?
how do you pass the password as encrypted to the server?
hello, gensalt needs a log_round. what is it?
what do I have to set?
>auto-complete and GWT:make the div visible and read out the values using the DOM Does that mean that we can't display login dialog in some fancy gwt way and should stick to the good old html if we want to use 'remember me' feature?
I recently started using GWT and was very surprised to see advice of sending token with each RPC call. Sending token in each RPC call doesn't look like efficient idea when JSP App Servers (Tomcat in my case) have pretty powerful concept of Sessions. So why would I invent wheel again ? It's unclear for me.
I understand that you don't want to use the sessionID that is sitting in the HTTP header for security reasons so you'd have this in the payload of the GWT-RPC. I am using Acegi with GWT-SL on the server and it looks to me like Acegi naturally puts a JSESSIONID into the header and is available in a cookie. Does anyone have suggestions on how to: a) server->client do something like generateSessionIDAndSendItBackToClient() but still use Acegi, and b) client-> pick out the JSESSIONID from the GWT-RPC payload and use it in Acegi's filters to check whether that JSESSIONID has a valid signed in user associated with it?
I copied and pasted "BCrypt.java" inside "client/util". When I try to hash the password using this utility class, I am getting this error and app is not loading.
ERROR? Line 19: The import java.security cannot be resolved
Do I need to configure anything in "<module>.gwt.xml"?
I have the same problem like k.ramins...
BCrypt.java lies within "client/helpers".
ERROR? Line 18: The import java.security cannot be resolved
Can anyone help?
Re: k.ramins and Schimki86 - if you are trying to include BCrypt under your GWT client (as suggested by the client/... paths (Otherwise, please pardon my assumption)), and then attempting to compile it to JavaScript?, it may be worth noting that GWT will only compile a subset of the standard Java libraries. java.security is not one of them. To get around this you have two options:
(1) move the hashing code into the whatever.server.whatever package from whatever.client.whatever - if your GWT interface doesn't depend on it, this should fix things
(2) modify BCrypt.java to remove the dependency on java.security.SecureRandom? (note that this will very likely decrease the cryptographic strength of your hash, and isn't optimal) - from my quick perusal of the code, there is only one place where it is used (the gensalt(int, SecureRandom?) method), and in that location it is only used to populate a byte array - you could replace that with calls to com.google.gwt.user.client.Random (the nextInt(int upperBound) method may prove useful) (java.io.UnsupportedEncodingException? will need to be replaced with a GWT-recognized Exception as well)
My bad, option 2 doesn't work - see the second post on this thread - it has to do with how JavaScript? handles numbers
Sorry, but I don't quite understand how to implement the "Remember Me" feature. If I will put the username\password text boxes in an invisible div element in the HTML, then how can I put that div element in the proper place\position within my GWT application, so that it shows in the right place when needed. Can anyone help on this?
Can you tar up this example and provide a download link as it would make things alot easier. The web is kind of sparse with gwt login examples and security. Thanks.
Thank you vulcanb, I will try as soon as possible...
Another question: where have I to implement the code for connecting and close connections to a database i.e. MySQL? In the implementation of the asynchronous service inside the server- package?
I tried out: hashing is now part of my server- package. There are no ERROR's now ;) Thats great and it works well.
I must be dumb, but I am having a hard time figuring some of these concepts out. Perhaps someone can help me with a few questions:
1) We're storing the SID in a client-side cookie and then the GWT app is grabbing that and sending it to the server with each RPC request. How is that any different than getting the SID from the cookie on the server side? Theoretically if an attacker can replace the cookie, then wouldn't the GWT portion of the code that reads the cookie to send it along with the RPC request pick up the replaced cookie anyway?
2) What is wrong with simply relying on the normal Tomcat (et al) method of handling sessions? Perhaps I simply wish to store some information once the client has logged in via an authentication back-end that I do not control. Why do I need to send the SID along with each request? The client and server already automagically connect each incoming client request with the correct session bag (or I thought this happened).
Thank you for your time!
Great article , i have been trying for days to figure out how to integrate GWT with JAAS, it seems that the answer is to just toss out JAAS.
Thanks, Shay
As for the auto-complete/auto-fill of the login form, see http://groups.google.fr/group/Google-Web-Toolkit/t/2b2ce0b6aaa82461
Can anyone please tell me how does the logout works.I want to log out an user and come to main page afterwards. Thanks in advance
sakutz,
1) The attacker cannot 'replace' the cookie. Since the browser automatically sends the cookie in the request header, the attacker can exploit this browser behavior but not actually have access to the cookie value or change it.
2) Because it does not protect against XSRF. BTW, if you are a developer of a secure application and things seem 'automagic' to you - you are in REAL TROUBLE.
Many commenting here do not appear to understand XSRF. Here is a contrived example:
Your customer forgets to log off from your website (http://mybank.com). He wanders to the attacker's website (http://badwebsite.com). This website has a link on it: http://mybank.com/transferMoneyServlet?toAccount=attackersAccount&amount=1000000000. If you do not have additional protection, the browser will AUTOMAGICALLY put the session cookie in the user's request to your website and your website will transfer money to the attacker.
Do you can send to me email with working example code for this FAQ with GWT and server part? Thank you)
auto-complete and GWT - You put a TODO - Can you please expand on this. I think that maybe I can create the form in HTML (without style and design - just fields) and maybe there is an option to load the form using GWT.
Please help.
Was wondering what would be a good(recommended) way to implement checkWithServerIfSessionIdIsStillLegal()? Is it that we store the sessionId when its first created along with the timestamp in a db. Then we can look at the current time and compare the difference to a certain value. Thanks, 3H.
"TODO: Explain how without https security is limited regardless, touch on bobv's proposal for running login outside of GWT over HTTPS"
I would really appreciate example code/configuration for how this is done. Can you help me?
Thanks /Per
A way to encrypt the hash of the password in the client instead of the server, is using javascript natively on GWT.
kk
There needs to be more info on using a sessionID.
How is the sessionID generated? is it just a random string that can be generated on the server side?
Thank you for this article. It's useful, great work
I found (and still find) This article is quite misleading, as it suggests that you can create a secure login process with GWT and doesnt specify that its not at all secure if you are not using HTTPS/SSL
If read in conjunction with: http://groups.google.com/group/Google-Web-Toolkit/browse_thread/thread/59f3aafcf4951523 it becomes a bit clearer
Specifically there is nothing about the problem of passing the password unencrypted from the client to the server. (I think this is what is meant when they talk about security being limited without HTTPS (or SSL) neither of which are yet available on GAE btw)
The natural thought from any newbie to this stuff is to assume you can just use Bcrypt on the Client before sending the password to the server. However, quoting Reinier from the discussion above "You CANNOT use jBCrypt in GWT. GWT plays fast and loose with numbers (they're all doubles), which means they don't overflow correctly. jBCrypt depends on these overflows."
Sadly as far as I can tell at present there does not appear to be any solution to this problem, and not even any "better than nothing" solution!
<STOP PRESS!: Im in the process of reading http://en.wikipedia.org/wiki/Digest_access_authentication which does indeed appear to be a "better than nothing" solution....>
Digest access didnt make a lot of sense to me and im not sure it really helps us either. In my searchings around on the internet I found this article about GWT/javascript security issues, which I thought might be useful for anyone else reading this wiki: http://groups.google.com/group/Google-Web-Toolkit/web/security-for-gwt-applications
Hashing on the client does not make sense at all. If the attacker is able to eavesdrop the password on the wire, he can then use this hashed password to log in again using a client with hashing removed.
@viliam: He doesn't even need to remove hashing (which is anyways not possible practically), he can just use the hash to login...
@viliam,sunnymax
Hashing on client side prevents the attacker from using the password on a different site. As the article itself states, "The vast majority of users 're-use' their passwords". Of course, if the other site uses the same hash mechanism, you could use the hash there. That is the reason, why salt is usually added to the password in the hash process.
Anyway, without proper encryption (https etc.), password or password hash should not be sent at all.
It is a great article to start. But if there are more examples with codes, it would be better. A few question. From article, "Your server will then validate this login, and return a sessionID to your GWT app." Is it like the following code?
public class LoginServiceImpl? extends RemoteServiceServlet? implements LoginService?{ public String login(String email, String passwd) { String sessId = this.getThreadLocalRequest().getSession().getId(); final long DURATION = 1000 60 60 24 14; //duration 2 weeksDate expires = new Date(System.currentTimeMillis() + DURATION); Cookie cookie = new Cookie("sid",sessId); cookie.setPath("/"); this.getThreadLocalResponse().addCookie(cookie); return sessId; } }
getThreadLocalRequest() returns null and hence I am unable to get hold of session/ID - any suggestions?
Some answers to questions:
log_round is 10 by default. This is a fine default.
You generate the session id as a random string of 32 random alphanumeric characters. You store this in the DB, including a separate DB timeout (some cleanup script should automatically run DELETE FROM sessions WHERE timeout > NOW() from time to time to clear out old entries). You could cache these things server-side if you want to avoid a DB roundtrip every time someone does anything (as you need to check, every time, if the session is valid). In practice, with an index on the session key, I don't think that's necessary, but YMMV.
You cannot send the password encrypted to the server. That part, if Eve is also looking at the packets flying across the network, you've compromised the user's password. Technically you can stop Eve by using some extremely fancy javascript-based hashing algorithms, but it is theoretically impossible to beat Mallory (somebody capable of not just watching the data but modifying it in transit) with just GWT. You HAVE to use HTTPS to beat him.
BCrypt is something for the server only, your client will never need it (and in fact can't, as java.security isn't part of the java subset you can use with GWT).
Why should your server not rely on the cookie value? Search wikipedia for CSRF. That'll explain why that's a bad idea.
Does anyone know what the author means by "Now you can run the following code right after your EntryPoint begins execution." Does this mean there are two GWT applications? Or one GWT application with two entry points? I do not understand where you can fit in the code below:
Does it go in the public void onModuleLoad() method?Berkan, he's just giving an example of an early enough point in your module, to check the user's SID.
Yes, this should occur in the onModuleLoad() before the user can perform any actions that require authorization.