Simple NodeJS Express CSRF (Free Download)

Welcome to a tutorial on how to implement simple CSRF token protection in NodeJS Express. So you have heard of “CSRF attacks” and want to add some form of protection to your project?

CSRF token protection in the simplest form:

  • Generate a random string (token) in the user session.
  • Put the token in a hidden field within the HTML form.
  • On submission, cross-check the submitted token against the session.

That’s all, but let us go through an actual working example – Read on!

 

 

TABLE OF CONTENTS

 

DOWNLOAD & NOTES

Here is the download link to the example code, so you don’t have to copy-paste everything.

 

EXAMPLE CODE DOWNLOAD

Source code on GitHub Gist

Just click on “download zip” or do a git clone. I have released it under the MIT license, so feel free to build on top of it or use it in your own project.

 

SORRY FOR THE ADS...

But someone has to pay the bills, and sponsors are paying for it. I insist on not turning Code Boxx into a "paid scripts" business, and I don't "block people with Adblock". Every little bit of support helps.

Buy Me A Coffee Code Boxx eBooks

 

WHAT IS CSRF?

Before we get into the code example, here is a quick section to help absolute beginners understand “what the heck is CSRF”.

 

CROSS-SITE REQUEST FORGERY

A type of malicious exploit of a website or web application where unauthorized commands are submitted from a user that the web application trusts.

– Wikipedia

Very simply:

  • There are 2 websites.
    • On the “good website”, there is a form to do a transaction.
    • On the “bad website”, there is a fake form that submits to the “good website”.
  • The “bad website” baits users into submitting the fake form, and does transactions without the user’s knowledge or consent.

An example is worth a thousand words, let’s go through a very simple CSRF attack below.

 

 

CSRF ATTACK EXAMPLE

GOOD WEBSITE

<form method="post" action="http://good-site/close">
  <p>Enter "CONFIRM" below to close your account.</p>
  <input type="text" name="confirm" required>
  <input type="submit" value="Go">
</form>

On the “good website”, we submit confirm=CONFIRM to http://goodsite.com/close to close the account. Of course, the user must be signed in.

 

BAD WEBSITE

<form method="post" action="http://good-site/close">
  <p>Enter your comment below.</p>
  <input type="text" required>
  <input type="hidden" name="confirm" value="CONFIRM">
  <input type="submit" value="Go">
</form>

On the “bad website”, we have a fake comment form. Take note of what it does, it submits confirm=CONFIRM to http://goodsite.com/close. So yes, if the user is signed in at “good website”, the account will be closed.

 

 

NODEJS EXPRESS CSRF PROTECTION

Now that you have a general idea of CSRF attacks, let us go through a simple prevention method – By using a “CSRF token”.

 

QUICK SETUP

  • Run npm i express express-session ejs cookie-parser body-parser to install the required modules.
  • There are 2 folders in this project:
    • public CSS, Javascript, images.
    • views HTML templates.

 

PART 1) THE HTML FORM

views/1-form.ejs
<form method="post" action="go" target="_blank">
  <!-- (A) HIDDEN HTML TOKEN -->
  <div>* This token should be hidden in your actual project</div>
  <input type="text" name="token" value="<%= token %>">
 
  <!-- (B) FIELDS AS USUAL -->
  <input type="email" required name="email" value="jon@doe.com">
  <input type="submit" value="GO">
</form>

First, let us start with the HTML form. Nothing “special” here, just your regular fields, and a hidden field for the CSRF token.

 

 

PART 2) EXPRESS SERVER

2A) INIT

2-server.js
// (A) LOAD REQUIRED MODULES
const express = require("express"),
path = require("path"),
bodyParser = require("body-parser"),
cookieParser = require("cookie-parser"),
session = require("express-session");
 
// (B) INIT EXPRESS SERVER
const app = express();
app.set("view engine", "ejs");
app.use(cookieParser());
app.use(bodyParser.json())
app.use(bodyParser.urlencoded({ extended: false }))
app.use(session({
  secret: "YOUR-SECRET-KEY", // CHANGE TO YOUR OWN!
  resave: false,
  saveUninitialized: true
}));

Next, the Express server itself. Don’t think the top half needs a lot of explanation – We load the required modules and set the middleware.

 

2B) PAGES & CSRF PROTECTION

2-server.js
// (C) HTML PAGES
// (C1) PUBLIC FOLDER
app.use("/public", express.static(path.join(__dirname, "public")))
 
// (C2) HTML FORM
app.get("/", (req, res) => {
  req.session.token = (Math.random() + 1).toString(36).substring(4);
  res.render("1-form.ejs" , { token: req.session.token })
});
 
// (C3) SUBMIT HTML FORM
app.post("/go", (req, res) => {
  // (C3-1) CHECK TOKEN
  if (req.session.token == null) { res.send("Invalid token"); }
  if (req.session.token != req.body.token) { res.send("Invalid token"); }
 
  // (C3-2) PROCEED ONLY IF TOKEN MATCH
  console.log(req.session.token);
  console.log(req.body);
 
  // (C3-3) REMOVE TOKEN & RESPOND
  req.session.token = null;
  res.send("OK");
});
 
// (D) START SERVER
app.listen(80, () => console.log(`Server running at port 80`));

As in the introduction:

  • (C2) When the user accesses the HTML form, we generate a random CSRF token in the session.
  • (C2) Then, pass the CSRF token into the HTML template (put in the hidden form field).
  • (C3) On submission, we cross-check the submitted token against the session. Proceed only if they match.

The end.

 

 

HOW THIS STOPS CSRF ATTACKS

How can a “random string in session” stop CSRF attacks?

  • The CSRF token is stored in the server, and “planted” into the HTML form.
  • Only the server and user have the token.
  • The “bad website” will have to get a hold of the token to do a CSRF attack.

Yes, CSRF tokens add a layer of security, but it’s not foolproof. If the “bad website” somehow gets hold of the token, attacks are still possible.

 

EXTRAS

That’s all for the tutorial, and here is a small section on some extras and links that may be useful to you.

 

THE THREAT IS REAL

Before you brush CSRF tokens away as “stupid and a hassle”:

  • What if the “bad website” submits a form that says “Let a third party access my account”?
  • Buy something without the user’s knowledge?
  • Transfer money?

Yep, having an extra lock is better than nothing.

 

 

MORE SECURITY CONSIDERATIONS

Before the “security experts” start to say “this tutorial is not professional” – This tutorial is a very simple example. It is meant to help beginners grasp the basics of CSRF. A lot more can be done to secure a system:

  • Enforce HTTPS.
  • Use better encryption methods, other than a “random string”…
  • Keep a timestamp when the token is created, and set an expiry time. E.G. If the token is more than 10 minutes on submission, it is invalid.
  • Implement 2FA, the user has to verify to process sensitive transactions.
  • Keep a log of the user’s IP address and device. If the user “teleports” or it is a different device, something fishy is going on.
  • Rate limit – Can only process a certain amount per day.
  • Notify – Send notifications to the user when sensitive transactions are made.

 

LINKS & REFERENCES

 

THE END

Thank you for reading, and we have come to the end. I hope that it has helped you to better understand, and if you want to share anything with this guide, please feel free to comment below. Good luck and happy coding!

Leave a Comment

Your email address will not be published. Required fields are marked *