-
Notifications
You must be signed in to change notification settings - Fork 0
Security Measures
You can delete your public key from the database and regenerate a new one, it will be updated on all clients, when you rejoin the chat room. For maximum security and privacy, you can delete every message that was encrypted with that key pair. Take a look bellow, how it is done.
You can delete every message stored in the database, which was encrypted with you public key.
Every time a user connects to SignalR, the authorization process is based on the session cookie, kept in the browser, generated by ASP .NET Identity. Then client/server do negotiations for the communication protocol to use, (WSS, AJAX, etc...). And the connection gets established.
If you loose connection for a brief second with the server, the connection gets dropped and the user gets a message from the client JS. The reconnection is simply done by refreshing the web page.
We have filtered every output from the Internet, through an HTML character escape regex-based function in the js.
function escapeHtml(unsafe) {//RegEx Check the string and returns a replaced one, with HTML Safe Characters (XSS Prevention)
return unsafe
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">")
.replace(/"/g, """)
.replace(/'/g, "'");
}
Server-side .NET's Razor View Engine escapes any unencrypted elements by default, but since messages are decrypted on the client, we make sure that every decrypted output is filtered to prevent possible XSS attacks. So the escapeHtml()
method is called both in real-time communications and database fetched messages.
All messages are encrypted client-side before they are send to the network. If somebody intercepts a message it cannot be decrypted without the appropriate private key. Also the message is digitally signed, so the contents of it cannot be forged.
In RSA, the standard says that you need to sign the hash of the plain text message. But because of the nature of our communication scenario, the messages tend to be short and repeated/send many times in a regular chat.
Also the Signature is meant to be decrypted with the public key. Which means anyone can extract the hash inside it. The purpose of signatures is to make sure the message is not tampered with/forged.
So if someone know the hashed value of the message "Hello", for example, in plain text, he can load all these common message hashes from a rainbow table and crack all the hashes which he extracted using the public keys.
Effectively allowing an attacker to eavesdrop the communications, without the user and system noticing.
To mitigate that, our system uses SHA-256
hash function on the Base64 of the encrypted message.
RSA always generates unique cipher for the same plain text value, Base64 guarantees 1:1
representation of the ciphertext, so the Base64 will always be unique. SHA-256
hash is long enough to prevent hash collisions, so the hash of the Ciphertext of the Base64 will ALWAYS be a unique value. Thus eliminating the use of hash salting, like in password secure storage. Also if a message is forged, the attacker must know the private key to sign it again. We assume that it is not possible, but if the Private Key is known, the hash cannot be reproduced even then, nor by the user.
The message, received by the server is a JSON array, which gets deserialized server-side and contains objects of MessageReceive
class:
The .NET String.IsNullOrWhiteSpace(String)
Method is called on every field to verify the payload,
if the verification is not passed, and Exception is thrown - The message payload is invalid
. Which gets logged as you can see on the picture bellow.
Also the Client is notified. The messages are not sent and not saved to the Database. In normal circumstances this validation is done client-side and Exception rarely triggered on the server. Which can be an alert for MITM attacks.
If the method returns
true
, the next round of Validations is done, which checks the message and signature length, if the lengths are larger that the constants in ChatHub.cs
. An exception is thrown: The message payload is too big
.Which gets logged on the Server as you can see on the second picture bellow. Also the Client is notified. The messages are not sent and not saved to the Database. In normal circumstances this validation is done client-side and Exception rarely triggered on the server. Which can be an alert for MITM attacks.
Client-side message length validation before send:
Server-Side Message Length Validation, if JS gets Bypassed:
By default we are using 2048 bit RSA keys. I have hardcoded the JSEncrypt generator on 2048 bits. To generate a longer key, use OpenSSL. Though, there is a hardcoded constant in the backend and the frontend script, which checks the length of the message, because a 2048 bit key can encrypt only 245 chars with its modulus. So you need to change that if you are going to use longer RSA keys:
const int MESSAGE_PAYLOAD_LEN = 345; //With RSA 2048 bit key, the max base64 string is of length exactly 345(+ "%" sign for whisper traffic)
const int SIGNED_MESSAGE_PAYLOAD_LEN = 344; //With RSA 2048 bit key, the max base64 string of the SHA-256 hash is exactly 344 chars long
const MESSAGE_LENGTH = 245; // the max RSA encryption length for 2048 bit key Modulus, if the message is longer, the library would fail
If the Payload is in the valid Length the next validation round takes place, it validates the sender and receiver User ID's. if they are Invalid an Exception is thrown: The message recipient or sender are invalid
. Which gets logged as you can see on the picture bellow. Also the Client is notified. The messages are not sent and not saved to the Database. In normal circumstances this validation is done client-side and Exception rarely triggered on the server. Which can be an alert for MITM attacks.
Server-Side Exception if the JS gets Bypassed:
If all validation pass, the message is send to clients and transaction committed to the Database.
As you can see, there are only encrypted messages stored in the database, because there is nothing unencrypted traveling among clients.