Efficient way to do payload signing in HTML5 SPA

403 Views Asked by At

I'm looking to implement some efficient (i.e. with good performance) logic that does payload signing in our web application. The goal is for the HTML5 client to have a guarantee that the contents of a received payload are indeed those that were generated by our backend.

We don't want to do payload hash generation with shared salt because the user can easily open the HTML5 source and find the salt phrase.

We have implemented RSA signing for now where our backend adds a payload signature using its Private Key and our HTML5 client validates it using its baked in Public Key. However the signature generation process takes 250ms (for a relatively small payload) and due to the nature of the signed request this amount of time is unacceptable.

The only other idea is to generate a shared secret at runtime every time a client initializes its session with the backend. The secret however can't be sent in plaintext form so it seems we're going to have to implement a Diffie-Hellman exchange mechanism, something we'd like to avoid if possible or automate with existing libraries.

Remember that the secrecy and encryption need to be done at the Application layer, due to the nature of how we sell our product. We're not looking to encrypt our traffic, this is something that our customers might or might not implement (since it's an intranet application). However, we have to avoid exposing stuff that are related to our licensing checking mechanisms etc to them. The backend is not cloud based and is not controlled by us, but installed on the customers' machines, on premises.

Frontend is Javascript and backend is Java.

2

There are 2 best solutions below

0
PentaKon On BEST ANSWER

We ended up by using TweetNaCl both on the client and on the server side. The library provides a every easy and fast way to do DH-like shared secret exchange without going through a custom implementation. With an ephemeral shared secret we can easily generate hashes instead of signatures for our payloads dropping from 250ms to 10μs. Also RSA signing the initial DH exchange is important and the only place we use RSA.

Please read @AlexandreFenyo answer for proper theory on how to usually handle such cases.

1
Alexandre Fenyo On

Note that Diffie-Hellman exchange mechanism is not protected against MITM attack, therefore not encrypting traffic means that you need to authenticate the DH data coming from the server. This is why a web server using a DH-based cipher suite signs the DH elements sent over the network with the private key of its server certificate, for the client to check that those elements are really from the server that he wants to connect to. Those elements are public but need to be signed.

What you call "payload hash generation with shared salt" is a keyed-hash message authentication code, so it is based on a shared secret, as you noticed, and since you do not want to use this mechanism, it means that you do not trust the client. Therefore, you have to use asymetric cryptography to sign your payload.

Signing a server payload with an asymetric algorithm means that you first need to let the server share a public key with the client. Since you do not encrypt data between the client and the server, you need to deploy the server public key inside the client source code.

You talk about the signature generation process, but the signature check process on the client side is also very important in your case, because the total time the user has to wait for the result is the addition of the time to sign and the time to check the signature (moreover, the signature can often be anticipated on the server, if the data to sign is not dynamically generated, but the verification can never be anticipated). So you need a rapid way of checking a signature on the client side. First, sign a hash, not the whole payload. Then choose the fastest asymetric signature algorithm that is available in your development environment, on the client side. Note that checking an RSA signature is faster that checking a DSA or ECDSA one, for respective keys length corresponding to the same security level. So you should stay with RSA.

All of this until this line may not help you so much! Now there is a way to increase the performances using RSA to sign and verify signatures, and this way is rather the same that SSL/TLS implements to increase browser performances when downloading multiple pages or other objects from the same server: use a session cache. You share a common secret for a specific session with one specific user. Never use this common secret for other sessions. When the user is connecting for the first time, use RSA only once, to exchange an ephemeral shared secret or exchange DH material to create this shared secret. Then, each time the server needs to sign an object, it creates a keyed-hash message authentication code with this specific secret. Therefore, if the user finds the secret, for instance using the debug mode of his browser, it's not a problem: this secret is only here to help him know that something that comes from the server has not been altered. So the user can not use this secret to alter data exchanges between the server and other users.