JAVASCRIPT
End-to-End Encryption in the Browser
End-to-end encryption is a security protocol that ensures the privacy of data exchanged between two or more parties. In the context of web browsing, end-to-end encryption can be implemented to protect sensitive information such as login credentials, credit card details, and personal messages from being intercepted and read by third parties.
In traditional web browsing, data is encrypted only between the user’s device and the server hosting the website. This means that anyone who can intercept the data between these two points can potentially read or modify it. On the other hand, end-to-end encryption encrypts the data on the user’s device and can only be decrypted by the intended recipient’s device, ensuring that even the server hosting the website cannot read the data.
This works well unless the server gets compromised. The attacker will have access to every single log! This is something we’d like to avoid.
End-to-End Encryption
End-to-end encryption is a sophisticated technique that enables multiple clients to communicate with one another without any possibility of the server intercepting or deciphering the content of their messages. The basic is that the message content is first encrypted before being transmitted to the server, which then simply stores the encrypted blob and returns it to the intended recipient without ever accessing the main data.
We Talked too much Let’s code
Fortunately, the Web Cryptography APIs are now widely available to all browsers that let us implement this. You can check which browser support that in this link That said, the APIs to deal with encryption, keys, and binary data are not the most straightforward, this next section walks you through how to wire it all together.
We generate a random key for use in encrypting the data.
const cryptoKey = await window.crypto.subtle.generateKey(
{ name: "AES-GCM", length: 128 },
true,
["encrypt", "decrypt"]
);
We use that random key to encrypt the content.
const encryptedData = await crypto.subtle.encrypt(
{ name: "AES-GCM", iv: new Uint8Array(13) },
cryptoKey,
new TextEncoder().encode(JSON.stringify(content))
);
Now We can send the encrypted data to the server. It is important to note that we do not provide the key to the server!
The server can return us an ID or URL. It’s up to you guys. In this case, I presume that the Server sends us an ID.
const response = await (
await fetch("/uploadToServer", {
method: "POST",
body: encryptedData,
})
).json();
// {Id:'5f1fd529c049259da7dcc3b2'}
In this case, we want a shareable URL to share with our friends the content.
const contentId = response.Id;
const contentKey = (await window.crypto.subtle.exportKey("jwk", cryptoKey)).k;
// this 'cryptoKey' object from up
const shareableUrl = '<http://localhost:3003/>' + contentId + "#sc=" + contentKey;
// Example: <http://localhost:3003/content?id=5f1fd529c049259da7dcc3b2#sc=rhX1rn9kKwak6nCeLVcHMQ>
From the other side of the Water 😛
We’ve sent the link and our friends will open the link. Let’s look at this situation.
We fetch the data from the server.
const response = await fetch(`/getFromId?id={id}`);
const encrypted = await response.arrayBuffer();
We need our cryptoKey
, It’s in the URL.
const shareableKey = window.location.hash.slice("#sc=".length);
const key = await window.crypto.subtle.importKey(
"jwk",
{
k: shareableKey,
alg: "A128GCM",
ext: true,
key_ops: ["encrypt", "decrypt"],
kty: "oct",
},
{ name: "AES-GCM", length: 128 },
false,
["decrypt"],
);
Thus, we can decrypt our content with that key. Decode it to a string, then parse it back into JSON.
const decryptedContent = await window.crypto.subtle.decrypt(
{ name: "AES-GCM", iv: new Uint8Array(13) },
key,
encrypted,
);
const decoded = new window.TextDecoder().decode(new Uint8Array(decryptedContent));
const content = JSON.parse(decoded);
Conclusion
End-to-end encryption enhances the security of our application by ensuring that data is protected throughout its transmission and storage. In the event of a security breach of the hosting service, our data remains intact since decryption requires the key, which is not accessible to unauthorized entities.
Stay tuned for more!