1

I have a traditional PHP site which uses sessions. I've developed a real-time app in nodejs and wish to authenticate users here based on their PHP session. The procedure would go something like this:

  1. Client AJAX's some getAuthToken.php page
  2. Client connects to the websocket server anonymously
  3. Client sends their authentication token
  4. Server checks token, authenticates user

getAuthToken.php should generate an authentication token for the current session's username using some symmetric key (shared by the websocket server) and return it.

What would be the best algorithm to use here? I have little experience with cryptography and not sure where to proceed.

One approach that comes to mind would be to SHA-2 hash the username along with some secret key. We'd then pass the username and this hash to the websocket server which would do the same and compare hashes. Include a timestamp with the hash to ensure a unique token each time.

Is the above approach secure? Is there a better approach? I'm not concerned with anything on the wire, we'll be using SSL for all communication - my only concern is the ability to impersonate other users by generating a bogus token through inspection of your own token. The algorithm must be supported in both PHP and node.

Surely there is some standard way of doing this?

1

Rather than just hash, you should encrypt some values that can be decrypted by both your PHP and Node.js systems.

The data to include could just be:

  • Username.
  • Ticket expiry date.

However, since you are encrypting you could include additional details you would need to be available and these would remain private from the end-user.

These would be encrypted using AES and a 256 bit symmetric key that is known only to your PHP and Node.js applications. For a rolling expiration you should update the expiry date on every request and issue a new ticket. For performance reasons you could update less than this (say once the ticket has reached its half life). This is the encryption method ASP.NET uses for its authentication tickets.

So your token could be

AES-256(256bit secret key, username + "|" + (date-time + 20 mins))

After decryption you can check that the date-time value is in an acceptable range (i.e. the next 20 mins) - if not you should log this as a security event and this will provide reasonable protection against brute forcing. If you're storing this in a cookie as well as sending it via WebSockets you can make the cookie expire at the same time (bear in mind though that not everyone will have their clock set correctly so you might want to add in some extra leeway for the cookie expiration).

If you want further protection, encryption does not provide integrity as @GZBK noted, so you may want to ensure integrity of the decrypted value. This can be achieved with a HMAC of the hashed data. So your token would become:

encrypted_data = AES-256(256bit secret key, username + "|" + (date-time + 20 mins))
ticket = encrypted_data + "|" + HMAC_SHA256(256bit secret key, encrypted_data)

You can use Base64 encoding to represent values within your ticket.

  • Encryption is not made to ensure data integrity, but data confidentiality. Here a client may be able to modify the message without the modification being detected. Encryption might be useful though if it was required to store data in a cookie which must remain unknown to the client, but in this case a locally stored session data would be more secure. – WhiteWinterWolf Jan 27 '15 at 21:08
0

Encrypting the data is not a requirement: this is just security by obfuscation and does not provide any real supplementary security.

What you are building is a session ticket: the getAuthToken.php provides a ticket to the visitor, then the visitor "shows" the ticket to the websocket server to proceed. The websocket will then need to perform two operation here:

  • Check some values in the ticket (is the timestamp not too old, has the user enough privilege to access this content, does the source IP mentioned in the ticket correspond to the source IP address from the current HTTP connection, etc.). These values are completely application dependent, they can be stored in a single cookie or in different ones, appear in clear form, all of this does not matter.
  • Check that none of these values have been altered, here lies all of the security of the ticket, and this can be done using a well known hashing algorithm (SHA2 for instance, but do not try to invent your own or customize it!) taking all of the informative data mentioned above and a secret key as input. This will be the signature ensuring the ticket authenticity.

For improved security, if both getAuthToken.php and the websocket can have access to some common database (not necessarily an SQL one), you may include a unique random unpredictable identifier in the ticket data and store it in this database: this will add another layer of security ensuring that the ticket has been generated on the server side and not (even very unlikely as long as the secret key is strong enough) forged on client side.

Edit (some more advices):

  • Be sure that the key is long enough to not weaken the hash (at least as long as the final hash size, you should generate it using a computer since human brain is usually quite poor in term of entropy, possibly change a few character afterward if you really do not trust your computer's PRNG),
  • Some languages provide an "hmac" version of hashing functions specific to this purpose (taking the key as a separate argument for better cryptographic security)
  • Even if it is not checked in a database, you may include a random "session" ID in your hashed data: it will add some noise which would prevent any potential statistic or timing analysis since the hacker will not be able to predict the exact string which will be computed by the server,
  • Feel free to change the secret key from time to time, depending on your architecture it may be quite a simple and low impact task which will void any ongoing work to bruteforce your key (the frequency completely depends on your paranoia level ;) )
  • Thanks for the input. I'd like to avoid maintaining a shared resource between the two platforms unless absolutely necessary. At the very least I will include a timestamp field to be checked but the client's IP is another good way to counter session hijacking. Token forgery is my real concern and you see to have confirmed my beliefs here: provided I use a strong hash (sha2) and a sufficiently long key it's computationally infeasible to forge tokens? – Abovestand Jan 27 '15 at 1:37
  • That's one of the goal cryptographic hashing algorithm are up to, so you can be safe that the weak point will not be here. I have edited my post to add some supplementary advices :). – WhiteWinterWolf Jan 27 '15 at 21:01

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.