Securing Your Node.js API with Encryption and Sending Dynamic IV to Client : AES-CBC

Node.js API with Encryption and Sending Dynamic IV to Client : Securing data transmitted over the network is crucial in today’s interconnected world.

Encrypting sensitive data helps prevent unauthorized access and ensures data integrity. In this article, we’ll leverage AES encryption along with dynamic IV generation to enhance the security of our Node.js API.

Agenda

In this article, we’ll explore how to secure a Node.js API using encryption and dynamic IV (Initialization Vector) to protect sensitive data.

We’ll create separate files for controller, middleware, and service, and we’ll provide examples of request and response bodies.

Additionally, we’ll demonstrate how to send and receive a dynamically generated IV key as a header, and we’ll create a client to consume the API while implementing encryption, decryption, and IV in the header.

Read Also : Step by Step CRUD API using Node.js and Mongoose

Setting Up the Project Structure

Let’s start by organizing our project into separate files for better maintainability:

project
│ app.js
│ package.json

└───controllers
│ │ productController.js

└───middlewares
│ │ encryptionMiddleware.js

└───services
│ │ productService.js

└───config
│ config.json

  1. Controller: Handles incoming requests and sends responses.
  2. Middleware: Implements encryption and decryption middleware.
  3. Service: Contains business logic and data manipulation.

Controller (productController.js)

// Import service functions
const productService = require('../services/productService');

// Get product detail
exports.getProductDetail = async (req, res) => {
    try {
        const productId = parseInt(req.params.id);
        const product = await productService.getProduct(productId);
        res.send(product);
    } catch (error) {
        res.status(500).send('Internal Server Error');
    }
};

// Create a new product
exports.createProduct = async (req, res) => {
    try {
        const { name, price } = req.body;
        await productService.createProduct(name, price);
        res.send('Product created successfully');
    } catch (error) {
        res.status(500).send('Internal Server Error');
    }
};

Middleware (encryptionMiddleware.js)

const crypto = require('crypto');
const config = require('../config/config.json');


//Encryption Middleware
exports.encryptionMiddleware = (req, res, next) => {
    const iv = crypto.randomBytes(config.ivLength);
    const key = config.encryptionKey;
    
    const originalSend = res.send;
    res.send = function (data) {
        const encryptedData = encrypt(JSON.stringify(data), key, iv);
        res.setHeader('IV', iv.toString('hex'));
        originalSend.call(this, encryptedData);
    };
    
    if (req.body && req.body.encryptedData) {
        const iv = Buffer.from(req.headers.iv, 'hex');
        const decryptedData = decrypt(req.body.encryptedData, key, iv);
        req.body = JSON.parse(decryptedData);
    }
    next();
};

function encrypt(text, key, iv) {
    let cipher = crypto.createCipheriv('aes-256-cbc', Buffer.from(key), iv);
    let encrypted = cipher.update(text);
    encrypted = Buffer.concat([encrypted, cipher.final()]);
    return encrypted.toString('hex');
}

function decrypt(text, key, iv) {
    let decipher = crypto.createDecipheriv('aes-256-cbc', Buffer.from(key), iv);
    let decrypted = decipher.update(Buffer.from(text, 'hex'));
    decrypted = Buffer.concat([decrypted, decipher.final()]);
    return decrypted.toString();
}

Service (productService.js)

// Example product data (for demonstration)
let products = [
    { id: 1, name: 'Product 1', price: 100 },
    { id: 2, name: 'Product 2', price: 200 }
];

// Get product detail by ID
exports.getProduct = async (productId) => {
    const product = products.find(p => p.id === productId);
    if (!product) throw new Error('Product not found');
    return product;
};

// Create a new product
exports.createProduct = async (name, price) => {
    const newProduct = { id: products.length + 1, name, price };
    products.push(newProduct);
};

Read Also : Mastering Code Quality and Security: How SonarQube and Black Duck Works

App.js

body-parser : it will require to parse body in form of json ,raw or text for incoming encrypted data.

if we are sending encrypted data as text then we need to replace req.body.encryptedData to req.body only.

const express = require('express');
const productController = require('./controllers/productController');
const encryptionMiddleware = require('./middlewares/encryptionMiddleware');
const bodyParser = require('body-parser');


const app = express();
app.use(express.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
app.use(bodyParser.raw());
app.use(bodyParser.text());
app.use(encryptionMiddleware);

app.get('/api/product/:id', productController.getProductDetail);
app.post('/api/product', productController.createProduct);

app.listen(3001, () => {
    console.log('Server is running on port 3000');
});

Config.js

module.exports={
    encryptionKey: "0123456789abcdef0123456789abcdef",
    ivLength:16
}
  

Create API Request

request-body-with-IV-as-header-in-nodejs-api-crypto
request-body-with-IV-as-header-in-nodejs-api-crypto
sending-encrypted-body-with-IV-as-header-in-nodejs-api-crypto
sending-encrypted-body-with-IV-as-header-in-nodejs-api-crypto

cURL

curl -X POST \<br>'http://localhost:3001/api/product/' \<br>--header 'Accept: <em>/</em>' \<br>--header 'User-Agent: Thunder Client (https://www.thunderclient.com)' \<br>--header 'iv: c35a74542c044d791be34316b53c5ce6' \<br>--header 'Content-Type: text/plain' \<br>--data-raw '9e89312e1c7066bed89a8b0ec7e8e1071e62cec462dc9338cd2aac67c429186f761e12c787260bb5125ddb855f4e905a857d62ad0449d75ca6c5352d250f654bbe321a853b75f46c6c674e6ad2f9a231182477eb71a2db73790a3b09fc55a824bfa383266ab937ddecc135e1f1cec72f29bf18df402e74f884a1c87675cd565265b4991101cf5083de67839f485d517c'

Read Also: What is NoSQL Injection attack in nodejs


Client Implementation

To consume the API with encryption, decryption, and IV in the header, we’ll need to implement similar functionality in the client. Below is a basic example using Axios:

const axios = require('axios');
const crypto = require('crypto');

// Encrypt data
const encrypt = (text, key, iv) => {
    let cipher = crypto.createCipheriv('aes-256-cbc', Buffer.from(key), iv);
    let encrypted = cipher.update(text);
    encrypted = Buffer.concat([encrypted, cipher.final()]);
    return encrypted.toString('hex');
};

// Generate IV
const generateIV = () => {
    return crypto.randomBytes(16);
};

// Dynamic IV in header
const iv = generateIV();

// Example key (should be securely stored)
const key = '0123456789abcdef0123456789abcdef';

// Request body
const requestBody = {
    name: 'New Product',
    price: 300
};

// Encrypt request data
const encryptedData = encrypt(JSON.stringify(requestBody), key, iv);

// Send request with encrypted data and IV in headers
axios.post('http://localhost:3000/product', {"encryptedData":encryptedData }, { headers: { IV: iv.toString('hex') } })
    .then(response => {
        console.log(response.data);
    })
    .catch(error => {
        console.error('Error:', error.message);
    });

FAQ on Node.js API with Encryption and Sending Dynamic IV to Client

1. What is encryption, and how does it work?

Encryption is the process of converting plaintext into ciphertext using an algorithm and a secret key. The ciphertext can only be decrypted back to plaintext using the same key and algorithm.

2. What are the two main types of encryption?

The two main types of encryption are symmetric encryption, where the same key is used for both encryption and decryption, and asymmetric encryption, where different keys are used for encryption and decryption.

3. What is symmetric encryption, and when is it typically used?

Symmetric encryption uses the same key for both encryption and decryption. It is typically used for securing data transmission within a trusted environment where the key can be securely shared between the sender and receiver.

4. What is asymmetric encryption, and what are its advantages?

Asymmetric encryption uses a pair of keys: a public key for encryption and a private key for decryption. Its advantage is that the public key can be freely distributed, allowing anyone to encrypt data, while only the holder of the private key can decrypt it.

5. What is a key in encryption, and why is it important?

A key is a piece of information used by the encryption algorithm to transform plaintext into ciphertext and vice versa. The security of the encryption system relies on the secrecy and randomness of the key.

6. How does encryption help protect sensitive data during transmission?

Encryption scrambles the data into an unreadable format during transmission, making it unintelligible to unauthorized parties who intercept it. Only those with the appropriate decryption key can decipher the encrypted data.

7. What is decryption, and how does it differ from encryption?

Decryption is the process of converting ciphertext back into plaintext using the appropriate decryption key. It is the reverse operation of encryption.

8. What are some common encryption algorithms used today?

Common encryption algorithms include Advanced Encryption Standard (AES), RSA, Triple DES, and Elliptic Curve Cryptography (ECC), among others.

9. What is end-to-end encryption, and why is it important?

End-to-end encryption ensures that data is encrypted on the sender’s device and can only be decrypted by the intended recipient, without being accessible to intermediaries or service providers. It enhances privacy and security by preventing unauthorized access to data.

10. What is a cryptographic hash function, and how does it relate to encryption?

A cryptographic hash function is a mathematical algorithm that converts an input (or “message”) into a fixed-size string of bytes, typically a hash value. Unlike encryption, which is reversible, hashing is a one-way process, making it suitable for tasks like password hashing and data integrity verification.

11. How does encryption contribute to data security in cloud computing?

Encryption helps protect data stored in the cloud by ensuring that it remains confidential and secure, even if the cloud service provider’s infrastructure is compromised or accessed by unauthorized parties.

12. What is the difference between encryption at rest and encryption in transit?

Encryption at rest refers to the encryption of data stored on storage devices, such as hard drives or databases. Encryption in transit refers to the encryption of data while it is being transmitted between devices or across networks.

13. Can encrypted data be decrypted without the encryption key?

In most cases, encrypted data cannot be decrypted without the encryption key. However, certain encryption algorithms and implementations may have vulnerabilities that could potentially be exploited to decrypt data without the key.

14. What is a key exchange protocol, and why is it necessary for secure communication?

A key exchange protocol is a method for securely exchanging cryptographic keys between parties to enable encrypted communication. It is necessary to establish a shared secret key without exposing it to eavesdroppers or attackers.

15. How does quantum encryption differ from classical encryption?

Quantum encryption uses the principles of quantum mechanics to secure communication by encoding information into quantum states. It offers the potential for unbreakable encryption due to the inherent properties of quantum mechanics.

16. What are some common challenges associated with encryption?

Common challenges associated with encryption include key management, performance overhead, compatibility issues between different encryption systems, and the potential for vulnerabilities in encryption algorithms.

17. Can encrypted data be intercepted or tampered with during transmission?

While encrypted data can be intercepted during transmission, it remains secure from unauthorized access as long as the encryption key is kept secret. However, encrypted data may still be vulnerable to tampering if the encryption algorithm or implementation is flawed.

18. How does encryption impact data storage and processing performance?

Encryption can introduce additional computational overhead, which may impact data storage and processing performance, especially in high-throughput or real-time systems. However, modern encryption algorithms are designed to balance security with performance.

19. What are some best practices for implementing encryption in software applications?

Best practices for implementing encryption include using strong encryption algorithms, securely managing keys, implementing secure key exchange protocols, and regularly updating encryption software to address known vulnerabilities.

20. How does encryption contribute to compliance with data protection regulations and standards?

Encryption is often a requirement of data protection regulations and standards, such as the General Data Protection Regulation (GDPR) and the Health Insurance Portability and Accountability Act (HIPAA). Implementing encryption helps organizations protect sensitive data and demonstrate compliance with legal and regulatory requirements.

Conclusion

Implementing encryption and dynamic IV in your Node.js API enhances security by protecting sensitive data from unauthorized access. By separating concerns into controller, middleware, and service files, and by properly handling encryption and decryption in both server and client, you can ensure the confidentiality and integrity of your data transmitted over the network.