How to encrypt data in JavaScript before network transfer?

Content verified by Anycode AI
July 21, 2024
Protection of sensitive information during transit over a network has been the premier line of concern in web development. If not encrypted, personal details, financial information, and confidential messages can be readily intercepted by entities with malicious intentions. This guide solves this problem by showing how data is to be encrypted in JavaScript before sending it over a network. The encryption will be robust by using modern cryptographic methods available to JavaScript, more precisely the Web Crypto API. It shows how one can enhance the security of their application to ensure the safety of users' data from any unauthorized access and further breaches. This is very elaborated on both client-side and server-side implementations for complete protection.

Let's dive right in and make this more conversational and friendly.

To safely send data over the internet in JavaScript, we need to follow a few steps: generate an encryption key, encrypt the data, and then decrypt it on the other end. The Web Crypto API is perfect for this! So, let me walk you through the process.

Generating an Encryption Key

First, we need to create a special cryptographic key to handle both encryption and decryption. Typically, we go with symmetric encryption because it's a bit simpler and faster. Here's a little snippet for generating that key:

async function generateEncryptionKey() {
  const key = await window.crypto.subtle.generateKey(
    {
      name: "AES-GCM",
      length: 256, // Can be 128, 192, or 256 bits
    },
    true, // Whether the key is extractable (i.e., can be used in exportKey)
    ["encrypt", "decrypt"] // Usage for the key
  );

  return key ;
}

Encrypting Data

Now that we have our key, it's time to encrypt some data. AES-GCM (Galois/Counter Mode) is a great choice because it keeps the data not only confidential but also ensures its integrity.

async function encryptData(key, data) {
  const encoder = new TextEncoder();
  const encodedData = encoder.encode(data);

  // Generating a random initialization vector
  const iv = window.crypto.getRandomValues(new Uint8Array(12));

  const encryptedData = await window.crypto.subtle.encrypt(
    {
      name: "AES-GCM",
      iv: iv, // Initialization Vector (IV)
    },
    key, // From generateEncryptionKey
    encodedData // Data to be encrypted
  );

  return {
    iv: iv,
    encryptedData: new Uint8Array(encryptedData),
  };
}

Sending Encrypted Data

When we want to send the encrypted data to the server, it's essential to include both the initialization vector (IV) and the encrypted bit. We’ll convert them to a format that’s easier to send (like a Base64 string).

async function sendData() {
  const key = await generateEncryptionKey();
  
  const dataToSend = "Sensitive data that needs to be encrypted";
  const { iv, encryptedData } = await encryptData(key, dataToSend);

  // Converting IV and encrypted data to Base64 for easier transfer
  const ivBase64 = btoa(String.fromCharCode(...new Uint8Array(iv)));
  const encryptedDataBase64 = btoa(String.fromCharCode(...new Uint8Array(encryptedData)));

  const payload = {
    iv: ivBase64,
    data: encryptedDataBase64,
  };

  fetch('https://your-endpoint.com/api', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify(payload),
  });
}

Decrypting Data (Server-Side)

On the server side, we need to decrypt the data. This requires having the encryption key and the initialization vector (IV). Let's walkthrough a simple decryption in Node.js:

Don't forget to install the 'crypto' module if you don’t have it yet.

npm install crypto

Imagine the server receives both the IV and the encrypted data from the client, along with a securely shared key.

const crypto = require('crypto');

async function decryptData(sharedKey, ivBase64, encryptedDataBase64) {
  const iv = Buffer.from(ivBase64, 'base64');
  const encryptedData = Buffer.from(encryptedDataBase64, 'base64');

  const decipher = crypto.createDecipheriv('aes-256-gcm', sharedKey, iv);
  let decrypted = decipher.update(encryptedData, 'binary', 'utf-8');
  decrypted += decipher.final('utf-8');

  return decrypted ;
}

// Example usage
const sharedKey = crypto.randomBytes(32); // This should be securely shared
const ivBase64 = '...'; // Received from the client
const encryptedDataBase64 = '...'; // Received from the client

decryptData(sharedKey, ivBase64, encryptedDataBase64).then((decryptedData) => {
  console.log('Decrypted Data:', decryptedData);
}).catch((error) => {
  console.error('Failed to decrypt data:', error);
});

Quick Recap

To summarize the whole adventure:

  • Generate an encryption key with window.crypto.subtle.generateKey.
  • Encrypt the data using AES-GCM mode with window.crypto.subtle.encrypt.
  • Send the encrypted data and initialization vector (IV) to the server.
  • Decrypt the data on the server using a compatible method (Node.js crypto module).

This approach keeps your data safe while it travels across the internet, shielding it from prying eyes. Just make sure your key management is top-notch because the security largely depends on those keys remaining secure and intact. Happy coding!

Have any questions?
Our CEO and CTO are happy to
answer them personally.
Get Beta Access
Anubis Watal
CTO at Anycode
Alex Hudym
CEO at Anycode