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
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
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
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.
<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.
and submit the form with some data. You can see this result as csrf token
is validated and correct.
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.
/* 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.
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
// 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
<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.
Now just re enable our csrf
middleware in App1. Un comment this line
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.
That's how a CSRF token protects in a real application. You can find the full source code here Github Code Link
You can follow me on my twitter here KrishnadasPC
Top comments (0)