How to prevent MITM attacks when implementing E2EE? - node.js

I'm working on a project where two clients can send files to each other via web sockets (using Socket.IO). Each chunk is encrypted with AES.
Currently, the clients connect to the server, they each generate an RSA public/private key pair on their devices, they then announce their public keys to the server which sends them to the other client, and this gets stored by said client. Before data is sent, it is encrypted with AES using a random key and a random IV, and the AES key is then encrypted using the other client's public key. The data is sent across, the other client then decrypts the AES key using their RSA private key, and finally decrypts the content using the decrypted AES key and saves it to a file on their disk.
The issue is that the server could easily just replace one client's public key with its own, and steal the data. The only solution I can think of is for the clients to contact one another and manually verify their public keys... I'm not sure how I'd go about automating this process. Services that provide E2EE seem to generate a matching code on each device, but I'm having trouble finding any information about how this is actually implemented, like how would two devices generate matching codes without talking to a server or each other in between, and if they do, then the server knows the code anyway right?
I've considered using WebRTC to send the public key from one client to the other without having the data go through the server, but I'd appreciate alternative approaches. Thank you in advance! :)

To prevent MITM, users are supposed to "manually compare public key fingerprints through an outside channel", as explained in this article regarding the Signal Protocol.
Usually, it means checking an hexadecimal string over a trusted communication: face to face, phone, ... Depending on your requirements, you might also consider that an attacker cannot access both your tool and emails at the same time and consider emails your trusted communication.

Related

How to make sure that only the authorized user can access a feature provided by the server?

We are building an android application and one of its features is to book a cab service provider's cab (say an Uber).
We have an application specific user ID. Let us call it AUID. To book the cab, the application would Post a request to server and send AUID along with other relevant information (like lat, long etc). How do I make sure at the server end that the request is indeed coming from the correct user and it is safe to book the cab? In the current form, if a third party gets to know the AUID of another person, the third party can book a cab on behalf of that person.
One of the solutions I thought of was using asymmetric encryption. The application would hold the public key and the server would contain the private key. Instead of sending the user ID to the server, we'll instead send an encrypted key where the key would be AUID + timestamp encrypted using the public key. We'll then decrypt the message using private key at server end to obtain the AUID. If the timestamp at server does not lie within a certain interval of the timstamp sent by the client, we reject the request.
Is this a safe enough approach? Is there any other practice widely followed for such scenarios?
What you propose is sensible: encrypt the AUID on the client app and verify on the server. As comments suggest, SSL is vital.
The problem is that if how to encrypt the AUID is in your app, it can be figured out by anyone dedicated enough.
You can drastically reduce the risks of fake requests by issuing a separate encryption key for each user. This means that if someone cracks your code, they can still only spoof from one account. However, once an attacker had decompiled your app, they could theoretically start new accounts, get a valid encryption key and spoof requests.
What you need for 100% reliability is some form of authentication which is not stored in the client app - like a password or TouchID on iOS or fingerprint api on Android M. So when a user orders a cab, they need to enter some piece of information which you also encode with the AUID and check on the server. That secret information is not stored in your app, so no-one can fake requests.
Requiring a password from a user is pretty inconvenient. Fingerprint scanning is much easier and probably acceptable. You could also use a trust system - if the user has ordered cabs before and everything was OK, they can order without special authentication. Using Trust together with individual encryption keys is pretty effective because anyone trying to spoof requests would need to do a successful order before being able to spoof - which is probably too much hassle for them.

Client Side Encryption Security of key

This may sound a bit naive but I am missing something and I want someone to enlighten me.
To prevent man in the middle attacks where your password in plain text could be easily read, client Side Encryption is done where the password is encrypted so a man in the middle can only find data which is gibberish for him. However, for the encryption, the server needs to send a key over the network.
So, my question is, if a man in the middle attack occurs, then he would be able to see the key as well which was sent for the encryption on the client side. With the key in his possession, the encrypted password can be very easily decrypted. Hence, the whole purpose of the encryption is defeated.
What am I missing here?
First, a precision: the attack you are talking about is not a MITM but passive eavesdropping. In a MITM, the attacker relays back and forth messages from the two parties.
Sending a symmetric key along with the encrypted data would of course be silly as an eavesdropper would sniff it as well as the data. This is why asymmetric encryption (aka public key encryption) is used.
In asymmetric cryptography, ciphers do not operate with a single key but with a key pair, composed of a public key and a private key. Public and private key are created together at the same time using a special algorithm and they are strictly connected to each other. The client encrypts the data with the server's public key, which is the only key that is shared publicly; this data can only be decrypted by the server with the server's private key. The communication from the server to the client is done in the same way.
You might want to read about PKC here:
https://enigmail.wiki/Introduction_to_Cryptography,_PGP,_and_Enigmail
https://en.wikipedia.org/wiki/Public-key_cryptography
It is worth noting that a MITM attack can actually happen at the moment one of the parties shares his public key with the other:
First, Alice asks Bob for his public key. If Bob sends his public key to Alice, but Mallory is able to intercept it, a man-in-the-middle attack can begin. Mallory sends a forged message to Alice that claims to be from Bob, but instead includes Mallory's public key.Alice, believing this public key to be Bob's, encrypts her message with Mallory's key and sends the enciphered message back to Bob. Mallory again intercepts, deciphers the message using her private key, possibly alters it if she wants, and re-enciphers it using the public key Bob originally sent to Alice. When Bob receives the newly enciphered message, he believes it came from Alice. [Wikipedia]
This is actually the weak point of PKC; the solution is either to use a centralized Certificate Authority (this is how it is done for the SSL certificates used in HTTPS), or use a shared Web of Trust. Both solutions have their advantages and drawbacks.

SSL: How the client decrypt message from server

I've read and watched a lot of articles and videos about SSL AES and RSA, but one thing is ALWAYS missing in every explanation( or I just don't get it ) is how the client decrypts sensitive data that comes from the server!(e.g. how much money you have)
I get it that your public key can encrypt anything and send it to the server and anyone can have it, but what do you do when you want to retrieve something from the server?
Does it comes just as plain text?
Any of the articles and videos point that out, they all just say that you have a private key that you shouldn't share and a public key that you can encrypt your messages and share it in the internet, but they don't say how the client makes a GET request with a encrypted message and decrypt it so it can be human readable.
As it says in this link about AES:
Asymmetric cryptography works by having two different keys, one for
encryption and one for decryption. It's also often called 'public key
cryptography' because it's possible to make one key public (allowing
someone to encrypt a message) while keeping the other private (only
the holder of the private key can decrypt the message encrypted with
its related public key).
Any help is welcome!
I will leave some links about web security that I found useful to learn:
https://www.coursera.org/learn/internet-history/lecture/L7HzI/security-integrity-and-certificate-authorities
If you want all the details grab a copy of SSL and TLS: Designing and Building Secure Systems. For a more arid lecture, read RFC2246 The Transport Layer Security (TLS) Protocol.
The short story is this: during the TLS/SSL handshake the client and the server exchange a secret (the PMS, pre-master-secret). This secret is used to derive session keys, initialization vectors and HMAC keys for use by client and server. Each one uses this keys to encrypt and sign everything send from it's side, and each one use the other's key to decrypt and validate the data sent by the other.
Nothing ever goes in clear text, in any direction.
Authorization and authentication based on the certificate used is a completely orthognal issue.

Encrypting text

I'm wondering if it's possible to encrypt text client side.
The situation is that I have a block of text I want to encrypt, and then save the encrypted text to a file. Only after a certain amount of time has passed will my program decrypt it. Another requirement I have is to be able to transfer the file with the encrypted text and be able to decrypt it with other copies of my program on various machines.
The problem is where to store the key. The only implementation I can think of that would work is if I store the key server side, and have it return the key after a certain time has passed.
Is there a way to do this client side (i.e. without any internet access)?
You can surely encrypt text on the client side, but the real problem is key management.
If the assumption is no Internet access, then you're going to have to package a certificate in the client that contains both keys, private to encrypt, public to decrypt, unless you use a symmetric key. Was going to suggest just generating a local, temporary symmetric key, but you state you must be able to decrypt the text on other machines that won't have it, so that won't work. At first blush, and maybe I'm overlooking something obvious, is packaging a cert on the client with public and private keys, and that introduces its own set of problems, eg protecting the cert with the private key.

What is a good browser level encryption product?

I'm looking for a product that can provide browser-level encryption of textual data before it's sent to a backend server. HTTPS can be the secure transport mechanism but we need the packet to be encrypted on the browser first. The data packet must end up in encrypted form on the backend database. Use case is: user fills out form on the browser, user submits the form data, before sending the data over the wire, the browser encrypts the data, the data ends up in the backend database in encrypted form, at some point in time, all of the data will be collected from the backend database, moved to a disconnected computer and decrypted at that point.
HTTP over SSL/TLS (HTTPS) is secure enough. It actually is as secure as it gets.
With SSL/TLS you can choose the cipher suite and adjust the encryption algorithms and key sizes according to your needs. 2048-bit RSA and 256-bit AES should be secure enough.
In addition to encryption, SSL provides authentication. AND it is natively supported by virtually every browser in existence. Please don't reinvent the wheel.
I agree that TLS is good enough for a lot of cases and I've written a little about it in my journey to see how it works. It does have some issues like governments being able to compel certificate authorities to grant certificates to allow man-in-the-middle attacks, but in general it's pretty good.
You might be interested in the design of systems like LastPass that encrypt things in addition to using TLS (details here). I would say that your best approach if you think TLS isn't good enough is to do something in addition to it rather than in lieu of it.
That's what HTTPS (SSL/TLS) is for. It should be plenty secure enough.
But as you state in your answer to my query, you want the form data to be stored in the database in its encrypted form. A quick Google search brings up JQuery.Gibberish-AES. It's a JQuery plugin that will do AES encryption and can utilise an RSA public key.
Using this you could generate a public/private key pair, keep the private key on the super-secret disconnected computer, and publish the public key with your form. You can then encrypt the data, store it in your database, and even if someone got root access to the database server they could decrypt the data because the secret key is on your secure disconnected machine.
I'm sure there'd be similar plug-ins/libraries for other client-side frameworks if you don't use JQuery.
I'd still use SSL/TLS for the connection, because that provides the server authentication (and client authentication if you want) to protect against a man-in-the-middle.

Resources