Fortify your REST API and enhance data transmission security with CipherShield. Dive into this guide for practical insights.
Software programs can communicate with one another through an Application Programming Interface (API). It is an essential component of contemporary software architectures like microservices architectures for REST API.
The process of shielding APIs from attacks is known as API security. Attackers are increasingly focusing their efforts on APIs due to their widespread usage and ability to access confidential software and data.
A crucial element of contemporary web application security is API security. Vulnerabilities in Rest API include failed authorization and authentication, no rate limiting, and code injection. Companies need to test REST API frequently in order to find vulnerabilities and fix them with security best practices.
Data transferred through APIs, usually between clients and servers connected over public networks, must be secured. This is known as API security.
APIs are used by businesses to connect services and move data. Encrypted, exposed, or compromised Rest APIs may reveal financial information, private information, or other sensitive data. For this reason, security is a crucial factor to take into account when creating RESTful and other APIs.
Security flaws in backend systems can affect APIs. Attackers may potentially compromise all API functionality and data if they manage to breach the API provider. If an API is not properly coded and protected, it may also be exploited by malicious requests.
Helmet.js stands as a robust, open-source JavaScript library designed to fortify your Node.js applications by seamlessly configuring essential HTTP headers. Functioning as an adept middleware for Express and its counterparts, Helmet takes the reins in automatically incorporating or eliminating HTTP headers to align with stringent web security standards.
While it’s crucial to acknowledge that Helmet isn’t a cure-all, it significantly raises the bar for potential attackers seeking to exploit known vulnerabilities. Its role is pivotal in shielding Node.js Express applications from prevalent security threats, including but not limited to Cross-Site Scripting (XSS) and the ever-persistent menace of click-jacking attacks. Helmet emerges as a stalwart guardian, making the task of compromising your application’s security notably more challenging for would-be malicious actors.
Without Helmet, private data is exposed by default headers returned by Express, leaving your Node.js application open to attack by hostile parties. By using Helmet in Node.js, on the other hand, you can defend your application against vulnerabilities in the Content Web API Security Policy, XSS attacks, and other security threats.
Let’s use an example to further investigate this query. We’ll set up an Express application for Node.js and examine the security provided by its default HTTP headers.
Step 1:
Setup Project
1 2 3 4 |
mkdir secure-api-demo cd secure-api-demo npm i init-y npm i express cors helmet |
Step 2:
Create server.js file and add code.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
const express = require('express'); const cors = require('cors'); const app = express(); const port = 5000; // Use the cors middleware with specific configuration app.use(cors({ origin: '*', methods: 'GET,HEAD,PUT,PATCH,POST,DELETE', credentials: true, optionsSuccessStatus: 204, })); // Define a middleware to handle CORS errors app.use((err, req, res, next) => { if (err.name === 'CorsError') { res.status(403).json({ error: 'CORS error: ' + err.message }); } else { next(); } }); app.get('/data', (req, res) => { const data = [ { id: 1, name: 'Item 1' }, { id: 2, name: 'Item 2' }, { id: 3, name: 'Item 3' }, { id: 4, name: 'Item 4' } ]; res.json(data); }); app.listen(port, () => { console.log(`Server is running on http://localhost:${port}`); }); |
Step 2.1: (Optional)
Download ngrok, extract it, and open the folder in the terminal. Then, launch the following command to host your local server with ngrok
Step 3:
Integrating Helmet into your Node.js Express app is simple. In case of problems, follow the official guide.
In your server.js file, import helmet with the following command:
1 |
const helmet = require("helmet") |
Now, register helmet in your Express application with the below:
1 |
app.use(helmet()) |
The Response Headers will contain the following headers
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
Content-Security-Policy: default-src 'self';base-uri 'self';font-src 'self' https: data:;form-action 'self';frame-ancestors 'self';img-src 'self' data:;object-src 'none';script-src 'self';script-src-attr 'none';style-src 'self' https: 'unsafe-inline';upgrade-insecure-requests Cross-Origin-Embedder-Policy: require-corp Cross-Origin-Opener-Policy: same-origin Cross-Origin-Resource-Policy: same-origin X-DNS-Prefetch-Control: off X-Frame-Options: SAMEORIGIN Strict-Transport-Security: max-age=15552000; includeSubDomains X-Download-Options: noopen X-Content-Type-Options: nosniff Origin-Agent-Cluster: ?1 X-Permitted-Cross-Domain-Policies: none Referrer-Policy: no-referrer X-XSS-Protection: 0 Content-Type: application/json; charset=utf-8 Content-Length: 15 ETag: W/"f-pob1Yw/KBE+3vrbZz9GAyq5P2gE" Date: Fri, 20 Jan 2023 18:15:32 GMT Connection: keep-alive Keep-Alive: timeout=5 |
Before Helmet
After Helmet
Step 4:
Configuring security headers in Helmet
Content-Security-Policy
1 2 3 4 5 6 7 8 9 10 |
// overriding "font-src" and "style-src" while // maintaining the other default values helmet.contentSecurityPolicy({ useDefaults: true, directives: { "font-src": ["'self'", "external-website.com"], // allowing styles from any website "style-src": null, }, }) |
Referrer-Policy
1 2 3 4 5 6 |
// setting "Referrer-Policy" to "no-referrer" app.use( helmet.referrerPolicy({ policy: "no-referrer", }) ) |
Strict-Transport-Security
1 2 3 4 5 6 7 8 |
app.use( helmet.hsts({ // 60 days maxAge: 86400, // removing the "includeSubDomains" option includeSubDomains: false, }) ) |
X-Content-Type-Options
1 2 3 4 5 6 |
app.use( // not loading the noSniff() middleware helmet({ noSniff: false, }) ) |
X-Frame-Options
1 2 3 4 5 6 |
app.use( // not including the frameguard() middleware helmet({ frameguard: false, }) ) |
If you want to omit the X-Frame-Options header entirely, you can disable the frameguard() middleware with the following:
1 2 3 4 5 6 |
app.use( // not including the frameguard() middleware helmet({ frameguard: false, }) ) |
Step 1: Frontend
For React:
Install Dependency
1 |
npm i crypto-js |
create file generateClientToken.js and Add code
1 2 3 4 5 6 7 8 9 10 11 12 13 |
const generateReferrerID = () => { const expirationTimeSeconds = 6; const secretKey = 'myKey'; try { const expirationTimestamp = Math.floor(Date.now() / 1000) + expirationTimeSeconds; const combinedKey = secretKey + "." + expirationTimestamp; const encryptedKey = CryptoJS.AES.encrypt(combinedKey, secretKey).toString(); const finalKey = encryptedKey + "." + CryptoJS.AES.encrypt(expirationTimestamp.toString(), secretKey).toString(); return finalKey; } catch (error) { console.error(error); } }; |
Update the headers for API calls
1 2 3 4 5 |
headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${apiKey}`, // Add this line if your REST API requires an authorization token 'X-Referer': generateReferrerID() }, |
For HTML:
Add Dependencies for crypto-js
1 2 3 4 5 6 7 |
<head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Frontend Example</title> <!-- Include CryptoJS from jsDelivr CDN --> </head> |
Body
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
<body> <button onclick="getData()">Get Data</button> <!-- Container to display the data --> <div id="dataContainer"></div> <script> const generateReferrerID = () => { const expirationTimeSeconds = 6; const secretKey = 'myKey'; try { const expirationTimestamp = Math.floor(Date.now() / 1000) + expirationTimeSeconds; const combinedKey = secretKey + "." + expirationTimestamp; const encryptedKey = CryptoJS.AES.encrypt(combinedKey, secretKey).toString(); const finalKey = encryptedKey + "." + CryptoJS.AES.encrypt(expirationTimestamp.toString(), secretKey).toString(); return finalKey; } catch (error) { console.error(error); } }; function getData() { // Replace 'http://localhost:5000/data' with the actual API endpoint const apiUrl = 'http://localhost:5000/data'; // Replace 'your-api-key' with the actual API key if required const apiKey = 'your-api-key'; // Get the container element const dataContainer = document.getElementById('dataContainer'); // Make a GET request using Fetch API with headers fetch(apiUrl, { method: 'GET', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${apiKey}`, // Add this line if your API requires an authorization token 'X-Referer': generateReferrerID() }, }).then(response => { if (!response.ok) { throw new Error('Network response was not ok'); } return response.json(); }).then(data => { // Clear previous content in the container dataContainer.innerHTML = ''; // Iterate through the data and append it to the container data.forEach(item => { const listItem = document.createElement('p'); listItem.textContent = `ID: ${item.id}, Name: ${item.name}`; dataContainer.appendChild(listItem); }); }).catch(error => { // Handle errors during the API call console.error('Error during API call:', error); }); } </script> </body> |
Step 2: Backend
Create a getValidateClient.js file.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
const CryptoJS = require('crypto-js'); const secretKey = 'myKey' const expirationTimeSeconds = 6; const validateKeyAndExpiration = (key) => { try { const [encryptedKey, encryptedTimestamp] = key.split('.'); const decryptedKey = CryptoJS.AES.decrypt(encryptedKey, secretKey).toString(CryptoJS.enc.Utf8); const decryptedTimestamp = CryptoJS.AES.decrypt(encryptedTimestamp, secretKey).toString(CryptoJS.enc.Utf8); const expirationTimestamp = parseInt(decryptedTimestamp, 10); const isValid = decryptedKey === secretKey + '.' + expirationTimestamp && Math.floor(Date.now() / 1000) < expirationTimestamp; return isValid; } catch (error) { console.error(error); return false; } }; const validateClient = async (req, res, next) => { const { headers } = req const key = headers['x-referer'] || '' const isValidHost = validateKeyAndExpiration(key); if (!isValidHost) return res.status(401).json({ error: "You don't have permission to access this resource" }); else next(); }; module.exports = validateClient; |
Update server.js code to add middleware to handle validate the client
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
const express = require('express'); const cors = require('cors'); const helmet = require('helmet'); // Add this line const validateClient = require('./getValidateClient'); const app = express(); const port = 5000; // Use the helmet middleware to enhance your app's security app.use(helmet()); // Use the cors middleware with specific configuration app.use(cors({ origin: 'http://127.0.0.1:5500', methods: 'GET,HEAD,PUT,PATCH,POST,DELETE', credentials: true, optionsSuccessStatus: 204, })); // Define a middleware to handle CORS errors app.use((err, req, res, next) => { if (err.name === 'CorsError') { res.status(403).json({ error: 'CORS error: ' + err.message }); } else { next(); } }); // Integrate the validateClient middleware for the /data endpoint app.get('/data', validateClient, (req, res) => { const data = [ { id: 1, name: 'Item 1' }, { id: 2, name: 'Item 2' }, { id: 3, name: 'Item 3' } ]; res.json(data); }); app.listen(port, () => { console.log(`Server is running on http://localhost:${port}`); }); |
Step 3
Make API call from FE
Console for BE
Testing via Postman
“Now equipped with an understanding of Helmet.js and its pivotal role in securing Node.js applications, let’s delve into the vulnerability inherent in default Express apps due to the absence of security HTTP headers.
Express APIs, lacking these headers by default, leave applications exposed to potential security threats. Fortunately, Helmet offers a streamlined solution. With just one line of code, seamlessly integrate Helmet into your Node.js application, establishing a formidable security layer. This not only addresses vulnerabilities in default Express apps but also shields your system from prevalent cyber threats.
Our exploration extends to the application of crypto-js for enhanced REST API for web api security. Utilizing crypto-js fortifies the protection of both API calls and the backend, thwarting unauthorized access attempts. This cryptographic library provides versatile tools, enabling the implementation of secure communication channels and data encryption. For organizations seeking expertise in custom financial software development, healthcare software development, and software development for financial services, our services cover a broad spectrum. Whether you’re in need of an iOS mobile app development company, cross-platform mobile app development services, or QA software testing services, we cater to diverse requirements.
Web Development Services in the United States