Nodejs is a runtime environment that allows developers to run javascript outside of the browser; it was first launched in 2009 and went on to become the most favoured choice among backend developers worldwide.
Security is the most important aspect of the internet, be it an app, game, or anything really. As a backend web developer, it's basically your responsibility to code securely and make sure everything is checked to prevent attacks on your web application.
Let's look at the best secure coding practices with nodejs
Strict-Transport-Security
With nodejs it is essential that you apply security headers whenever necessary. You are telling the browser to only communicate using HTTPS instead of HTTP when communicating with a domain. Let me explain a little bit better.
In a normal communication flow with a website to make an HTTP request to the domain, if the website uses an SSL/TLS certificate, an HTTPS rerouting is enforced and will redirect to the same site with an HTTPS request. Strict Transport Security ensures that all communication between the client and server is processed through HTTPS.
To put all of this in code, it would be:
strict-transport-security:max-age=25563000;
In this code, the max-age signifies the allowed time in seconds for the browser to remain that the site to be only accessed using HTTPS
If you're going to use it in a code, it would be like this
app.use(function(req, res, next){
if (req.secure) {
res.setHeader('Strict-Transport-Security', 'max-age=25563000; preload');
}
next();
})
Use SSL/TLS
This goes without saying, right? Sending your data over HTTPS is now an industry standard when it comes to security, Especially if your app transmits sensitive data, like passwords, credit card information and other information.
SSL certificates mitigate cyber-attacks, including packet sniffing, eavesdropping, and man-in-the-middle attacks. Ideally, you would want to get a multi-layered secure SSL certificate from a third-party website if you're working on something that collects very sensitive data. You can also generate a self-signed certificate with OpenSSL.
openssl genrsa -out server.key 2048
Now that we have the SSL signed, let's redirect all HTTP requests to HTTPS
const fs = require('fs');
const https = require('https');
const express = require('express');
const NODE_ENV = process.env.NODE_ENV || 'development';
const PORT = process.env.PORT || 3443;
const app = express();
https.createServer({
key: fs.readFileSync('/path/to/key.pem'),
cert: fs.readFileSync('/path/to/cert.pem')
}, app).listen(PORT);
// Redirect http requests to use https in production
if (NODE_ENV === 'production') {
app.use((req, res, next) => {
if (req.header('x-forwarded-proto') !== 'https') {
res.redirect(`https://${req.header('host')}${req.url}`);
} else {
next();
}
});
}
OpenSSL is a little bit more complex, so here's a complete guide by DevOps cube should you be interested in learning about it.
XSS attacks
Cross-site scripting is a common type of injection and attack in which malicious scripts are injected into trusted websites. An attacker can use XSS to send a malicious script to a user. If there's no way to check the script, a user might not know if it's from an untrusted source and just execute it.
There's no telling what the script can access; sensitive information would not be safe from this script, and browser cookies, sessions, and tokens are also at risk of being accessed.
So, yeah. We don't want to be joking about XSS attacks, and we must ensure that we validate and sanitize user data before processing or storing it in the database. Never underestimate the power of validation.
The common liberties used to prevent this are XSS filters.
First, run the following line on your cmd; we have to install xss-filters before we can use it.
npm install xss-filters --save
var express = require('express');
var app = express();
var xssFilters = require('xss-filters');
app.get('/', function(req, res){
var firstname = req.query.firstname;
res.send('<h1> Hello, ' + xssFilters.inHTMLData(firstname) + '!</h1>');
});
app.listen(3000)
SQL Injections
Multiple vulnerabilities can occur with SQL programming; as a backend developer, it's essential to understand what SQL vulnerabilities there are and how to stop them.
For you to open yourself to an SQL Injection attack, you have to pass invalidated user input directly to a SQL statement.
Let's look at a brief example:
Assuming you have written code that retrieves user information with the following code
query = 'SELECT + FROM Users Where Email = "' + USERNAME + '" AND Pass = "' + PASSWORD + '";'
This query means to search through the entire catalogue and finds the user with those details that intend to log in. However, a hacker could exploit this by inputting specific values the developers did not consider. Putting something like ""=" would immediately return all the users in the table, and that's extremely bad for business.
To prevent this, you must add a layer of validation to your code to avoid exploiters from inputting things you don't want.
app.post("/users", (request, response) => {
const data = request.body
const query = `SELECT * FROM Users WHERE id = (${data.id})`;
connection.query(query, (err, rows) => {
if(err) throw err;
response.json({data:rows});
});
});
As we've previously mentioned, you cannot allow unsanitized input methods. The code above now will find users' information based on the IDs provided.
A basic validation sample would be :
function validateForm() {
let x = document.forms["form"]["email"].value;
const re = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
This simple code would prevent any unwanted characters from being inputted that would mess with the SQL structure of your application
Obviously, these are very, very basic validation methods. To read more on this, read this blog on SQL validation methods with nodejs.
Brute Force and DoS Prevention
Brute force is the most common type of attack. As a backend developer, you must take into consideration brute-force attacks. We must prevent our application from an extremely large number of requests.
To do this, we must use a limiter to reduce the number of requests we get on our application. We must first install express-rate-limit
npm install express-rate-limit
import rateLimit from 'express-rate-limit'
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // Limit each IP to 100 requests per `window` (here, per 15 minutes)
standardHeaders: true, // Return rate limit info in the `RateLimit-*` headers
legacyHeaders: false, // Disable the `X-RateLimit-*` headers
})
// Apply the rate limiting middleware to all requests
app.use(limiter)
Error-Handling
Error handling is very important when you're creating your application. By handling errors, you get to display a proper message instead of allowing the program to display sensitive server information to the general public.
try {
//Block of code to try
}
catch(err) {
//Block of code to handle errors
}
Obviously, this error handling is an extremely complex topic. I recommend this article by LogRocket on Error handling to read more on this.
Conclusion
Every back-end web developer must always make sure they understand the basics when it comes to security. We want to ensure that your application is safe from basic cyber attacks.
If this article helped you, please follow me on my Twitter, and I'm most active there.