With SmartFoxServer 2.10 we have added full TLS encryption to the protocol. In this recipe we’re going to discuss the implications for the Login phase in SFS2X and the differences with the previous modality of secure Login.
» CHAP vs TLS
The Login phase in SmartFoxServer 2X has always been secure, even prior to the introduction of protocol encryption. For those not entirely familiar with the previous system we used a well known communication scheme called CHAP (Challenge-Handshake Authentication Protocol) which can be summarized in these steps:
- the client obtains a unique token upon connection
- the user’s password is hashed together with the unique token and sent over the wire
- the server retrieves the original password from a datasource (e.g. database), hashes it with the same token and compares it what was sent by the client
- If the two hashes are identical the password is correct
This system is pretty secure as it never transmits the password in clear and the hash will be different in every session, but has one main drawback: it typically forces the developer to keep the users’ password either in clear or hashed in the database, not allowing for proper salting.
Using a simple hashing pass is still not good enough, as the passwords need to be quite strong in order not be cracked using a reverse-hash database.
With the TLS-encrypted protocol we can overcome these issues because we’re no longer using an asymmetric encryption technique (i.e. hashing). Instead we’re using symmetric cryptography (AES 128) with a key that has been exchanged securely over HTTPS.
This in turn means that the password no longer gets to the other side as a hash which caused the storage limitations in the first place.
» What does this all mean?
In essence it means that clients’ passwords can be store in your datasources in whatever way your security requirements dictate.
If you want to get more in-depth on the new SFS2X protocol encryption, we recommend reading our introduction to SmartFoxServer’s TLS support.
» Can I use my old Login code with the new TLS protocol?
No, there a couple of minor changes that need to be applied to your code. It is all very simple and it all boils down to one single parameter which, unsurprisingly, is the password itself.
Let’s review the pre-2.10 modality of sending a login request.
Client side (C#) we used this:
sfs.Send(new LoginRequest("user name", "password", "zone name"));
Server side:
public class LoginEventHandler extends BaseServerEventHandler { @Override public void handleServerEvent(ISFSEvent event) throws SFSException { String name = (String) event.getParameter(SFSEventParam.LOGIN_NAME); String pass = (String) event.getParameter(SFSEventParam.LOGIN_PASSWORD); if (getApi().checkSecurePassword(session, clearPass, encryptedPass)) { // Login success... } else { // Create the error code to send to the client SFSErrorData errData = new SFSErrorData(SFSErrorCode.LOGIN_BAD_USERNAME); errData.addParameter(name); // Fire a Login exception throw new SFSLoginException("Login error!", errData); } } }
With SmartFoxServer 2.10 and higher we need to send the password as a different field in the request. This is because the 2nd parameter in the LoginRequest is always hashed with the unique token (as we said in the opening of this article) to maintain compatibility with the previous version.
To bypass the default password field we can simply pass a null value and provide the real user password as a field of the SFSObject taken as an optional 4th parameter in the request.
The object is a convenient tool to send any additional custom parameter at login time.
LoginRequest(string userName, string password, string zoneName, ISFSObject parameters)
» Secure Login 2.0, using TLS encrypted protocol
Before we move ahead with the code make sure that the Encryption is active in your Zone. If you need guidance for setting this up, please refer to the documentation here.
This is the client side code (C#):
ISFSObject params = new SFSObject(); params.PutUtfString("password", "MyPassword123"); sfs.Send("user name", null, "zone name", params");
The only difference here is that we’re wrapping the password in the SFSObject sent as the 4th parameter in the request.
On the server side we can use this code:
public class LoginEventHandler extends BaseServerEventHandler { @Override public void handleServerEvent(ISFSEvent event) throws SFSException { String name = (String) event.getParameter(SFSEventParam.LOGIN_NAME); ISFSObject params = (ISFSObject) event.getParameter(SFSEventParam.LOGIN_IN_DATA); String password = params.getUtfString("password"); if (password != null && password.equals(originalPassword)) { // Login success... } else { // Create the error code to send to the client SFSErrorData errData = new SFSErrorData(SFSErrorCode.LOGIN_BAD_USERNAME); errData.addParameter(name); // Fire a Login exception throw new SFSLoginException("Login error!", errData); } } }
The difference here is that we first obtain the SFSObject from the request and then proceed by extracting the password. Then we can use a regular String comparison between what has sent the client and original password retrieved from our data source.
Naturally, while we can access the password in clear in our Extension code, the data has been traveling trough the wire fully encrypted.
» Using the LoginAssistant Component with a custom password
For those using the more convenient LoginAssistant we have also updated the component with release 2.10 to make sure the same operation described above is also supported.
Please check the “Custom password checking and encryption” section of this doc page, to learn more how to use it in conjunction with TLS encryption.