The Must-Have Security Checklist for Web Apps

The Must-Have Security Checklist for Web Apps

As you progress in developing more complex apps, you will realize that the internet is a dangerous place! And it is! We see every so often news about data breaches, cyber attacks and other security threats that compromise the user's personal data and safety on the internet.

1.PNG Source:

This means one thing:

Cybersecurity is not something to be taken lightly!

In today's post, I'll sharing some types of common security threats on the web and how you can deal with them by following a security checklist for web apps.

Types of Common Security Threats

1. Cross-Site Scripting

Cross-Site Scripting or XSS is a type of attack where the hacker injects client-side scripts through a website. The server will mistakenly process the malicious script as their own and send the hacker confidential user data. This enables the hacker to log in to the site as the user, steal any personal information like the user's credit card details and change their password.

2.webp Source:

2. Injections

Injection vulnerabilities occur when someone injects data that is executed as a code by the interpreter and allows the person to access, modify and steal unauthorised data from the app. Common types of injection threats are SQL, LDAP (Lightweight Directory Access Protocol) and CRLF (Carriage Return Line Feed).

For example, a typical SQL statement can look like:

statement = "SELECT * FROM users WHERE name = $username

A normal user will enter their username and the code will return their account information that corresponds with that entered username.

But for someone with a malicious intent, they can input their username as

'any-name'; SELECT * FROM userinfo WHERE 'all' = 'all';

In this SQL statement, the attacker essentially completes the first query by giving some random name. But then, they started a new query to select and retrieve all the data from userinfo.

Below is a visual illustration of an SQL injection from 2.png

3. Cross-Site Request Forgery

CSRF for short, is a type of attack that occurs when a hacker sends a link (via email or social media) to a user who mistakenly thinks it is from a legit source (i.e. their bank). The user will be tricked to logging into their e-banking account to perform some actions like changing their email/password or making a funds transfer (i.e. making a request), therefore resulting in the attack. The hacker can now gain full access to the user's data.

3.png Source:

4. Brute Force Attacks

This type of attack happens when an attacker repetitively tries to access user accounts by trying different combinations of passwords and usernames using an automated system. It is like a burglar trying on a thousand different keys on the front door of every house in the neighbourhood to find the one that opens with his keys.


These are examples of the most common types of web security threats. You may read more at:

The Security Checklist

Now that we've learn some of the most common security threats, let's fight against them by following this checklist.

1. Secure HTTP Headers

For securing your Express apps, using Helmet is the most convenient way to do so.

Run npm install helmet --save and then include the following

const express = require('express')
const helmet = require('helmet')

const app = express()


// ...

(Code from

By default, Helmet will secure your app to prevent XSS attacks, MIME-sniffing, clickjacking and enforce HTTPS over HTTP connections.

You can configure more options by reading their documentation.

2. Authentication

Using an npm package called ratelimiter, you can prevent Brute Force Attacks by setting a limit to the number of tries a user can try to login.

Install by running npm install ratelimiter --save then limit the number of times for user._id

let id = req.user._id;
let limit = new Limiter({ id: id, db: db });
limit.get(function(err, limit){
  if (err) return next(err);

  res.set('X-RateLimit-Remaining', limit.remaining - 1);
  res.set('X-RateLimit-Reset', limit.reset);

  // all good
  debug('remaining %s/%s %s', limit.remaining - 1,, id);
  if (limit.remaining) return next();

  // not good
  var delta = (limit.reset * 1000) - | 0;
  var after = limit.reset - ( / 1000) | 0;
  res.set('Retry-After', after);
  res.send(429, 'Rate limit exceeded, retry in ' + ms(delta, { long: true }));

(Code from

For more details, read their documentation.

3. Cookies

Securing your cookies is how you make sure only authenticated users themselves can re-sign in to their account. The cookie-session npm package sounds delicious, and it can be installed to easily prevent cookie or session-related attacks.

Install with npm install cookie-session --save and to set up a simple view counter:

var cookieSession = require('cookie-session')
var express = require('express')

var app = express()

app.set('trust proxy', 1) // trust first proxy

  name: 'session',
  keys: ['key1', 'key2']

app.use(function (req, res, next) {
  // Update views
  req.session.views = (req.session.views || 0) + 1

  // Write response
  res.end(req.session.views + ' views')


(Code from

Important note: The 'keys' list should be stored in an .env file for protection. See documentation for more details.

4. Cross-Site Request Forgery

For Node.js apps, using a Node.js CSRF protection middleware like csurf is an effective way to prevent CSRF attacks.

Install with npm install csurf and on your route handler:

var cookieParser = require('cookie-parser')
var csrf = require('csurf')
var bodyParser = require('body-parser')
var express = require('express')

// setup route middlewares
var csrfProtection = csrf({ cookie: true })
var parseForm = bodyParser.urlencoded({ extended: false })

// create express app
var app = express()

// parse cookies
// we need this because "cookie" is true in csrfProtection

app.get('/form', csrfProtection, function (req, res) {
  // pass the csrfToken to the view
  res.render('send', { csrfToken: req.csrfToken() })
})'/process', parseForm, csrfProtection, function (req, res) {
  res.send('data is being processed')

Then, on the front end, you can set the csrfToken as the value of a hidden input element.

<form action="/process" method="POST">
  <input type="hidden" name="_csrf" value="{{csrfToken}}">

  Favorite color: <input type="text" name="favoriteColor">
  <button type="submit">Submit</button>

(Code from

The csurf package is compatible for both Express or AJAX apps.

Check documentation for specifications.

5. Injection Risks Management

To secure SQL or other command injection threats, various tools like sqlmap can be used to automate the process of detecting and eliminating SQL injection flaws.

Another option is to escape characters that allow the interpreter to change its intended use. Like making sure that any "username" or "id" fields cannot accept ' or " or ; as these symbol can have some meaning in SQL that may change its behaviour.

Here are more special characters that should be escaped.

4.png Source:


And that's all for now! The security checklist can by no means protect your app 100% because there are so many types of vulnerabilities that we can't cover all in this one post. Additionally, security threats are continously evolving. As developers, the least we could do is NOT to fall victim to the common ones listed above. Following this security checklist will more or less reduce threats very significantly and keep your app generally safe.

For a more detailed checklist, I recommend doing more reading on the topic and I found OWASP's cheatsheet series to be quite extensive and helpful!

Thanks for reading, stay safe. Cheers!

Did you find this article valuable?

Support Victoria Lo by becoming a sponsor. Any amount is appreciated!