How CSRF Token protects your web application
Tags: node, express, security
Published: 2021-09-20 08:48:54

We are going to look how CSRF tokens works in a real node express application and how it protects the app from cross site request forgery. If you would like to see the video version of this post you can watch it here.
[Watch video here](https://www.youtube.com/watch?v=l3CC4mPGFmw)


# What is cross site request forgery?

When a web server is receives a request from a client without any mechanism for verifying that it was intentionally sent, then it might be possible for a hacker to send requests to the web server which will be treated as a genuine request. This can be done via a Form submit, URL, image load, XMLHttpRequest, etc. and can result in data breach or unintended code execution. In this post we are going to explain a form submit kind of attack and how CSRF tokens prevents this.

## Code set up

There are two fresh express applications running. One is running on port `3000` which is **App1** and another is running on port `5000` which is **App2**. Both of these applications are created using the express generator. 

## App1 Code snippets

Install the npm `csurf`

Enable the csrf middleware for the application in `app.js`
```javascript
var csrf = require('csurf')
// setup route middlewares
app.use(cookieParser('fsgdesgsdYYFCCXXX'));
app.use(csrf({ cookie: true }))
```
Setting up the routes for the App1. Code from `routes/index.js`

```javascript
var express = require('express');
var router = express.Router();

/* GET home page. */
router.get('/', function(req, res, next) {
  res.render('index', { title: "App1, CSRF Demo", csrfToken: req.csrfToken() })
});
router.post('/process', function (req, res) {
  res.send('csrf is valid, data is being processed')
})
```
In the above code we are generating the `csrfToken` and passing it to the view where we load our form with `csrfToken: req.csrfToken()`

In the view we use handlebars as our templating engine and csrf token is added as a hidden input field.

```html
<h1>{{title}}</h1>
<p>Welcome to {{title}}</p>
<form action="/process" method="POST">
  <input type="hidden" name="_csrf" value="{{csrfToken}}">
  
  name: <input type="text" name="name">
  <button type="submit">Submit</button>
</form>
```
When we start the **App1** we can see a form loaded with the generated csrf token if you check the html view source of the page.
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ytq4yloi4sobzs0wc9vg.png)

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5b3jdvrpzk2fuvxb64pj.png)


and submit the form with some data. You can see this result as `csrf token` is validated and correct.
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/harpuqw246qqlinmzilo.png)

# How token protects the app?

Now let's remove the token generation and see how this CSRF middle-ware protects our application. Change the code in `app1/routes/index.js` like this. Now we pass `csrf` as an empty string.

```javascript
/* GET home page. */
router.get('/', function(req, res, next) {
  res.render('index', { title: "App1, CSRF Demo", csrfToken: '' })
});
```

Now if you submit the form you will get an error message like this.
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/u5qxl7k09201q4rjhzv3.png)

Now server rejects the request as it cannot find a valid token in the request and now we are protected from such kind of attacks.

# Attack request from App2

Now let's mimic an attack from another domain/application. In this example which is **App2**

For the time being just disable the `csrf` middleware in **App1**. Comment these lines in `app1/app.js`

```javascript
// app.use(csrf({ cookie: true }))
```

In **App2** home route we also have the same form but the form submit action is the url of **App1**
```html
<h1>{{title}}</h1>
<p>Welcome to {{title}}</p>


<form action="http://localhost:3000/process" method="POST">
  
  name: <input type="text" name="name">
  <button type="submit">Submit</button>
</form>
```
Start the server **App2** which is running on port 5000. You can see the form but now when you submit the form it is accepted as **App1** currently do not have CSRF middleware enabled.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4de40i6iaq5672hpcpx1.png)

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/351hne14b1ivpivvi0bx.png)

Now just re enable our `csrf` middleware in **App1**. Un comment this line
```javascript
app.use(csrf({ cookie: true }))
```

And now when you submit the form from **App2** you will get this error page only. So we are now protected.
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j3deyg1yqeturygr8vhm.png)

That's how a CSRF token protects in a real application. You can find the full source code here [Github Code Link](https://github.com/pcktuts/csrf-demo-express-js)

You can follow me on my twitter here [KrishnadasPC](https://twitter.com/pckrishnadas88)