Cross-Site Request Forgery (CSRF) is a type of malicious attack where a web application is tricked into trusting a user's browser to transmit unauthorized commands. CSRF exploits the user's active session to perform unwanted actions on their behalf. This tutorial will guide you through implementing CSRF protection in your Express.js applications.
Understanding CSRF Attacks
CSRF attacks occur when a malicious website, email, or application causes a user's web browser to perform an unwanted action on a site where the user is authenticated. CSRF tricks authenticated users into submitting unwanted requests. These attacks can lead to serious consequences such as:
- Changing user account details
- Making purchases without consent
- Deleting important data
How CSRF Protection Works
CSRF protection uses tokens to verify legitimate requests. It works by:
- Generating a CSRF token on the server.
- Embedding the token in every HTML form or AJAX request.
- Verifying the token on each state-changing request.
If the token is missing or incorrect, the server denies the request.
Setting Up CSRF Protection in Express.js
Express.js doesn't include CSRF protection by default. Use the csurf
middleware to secure your application.
Step 1: Install Required Packages
npm install express cookie-parser csurf
Step 2: Basic Setup
Here's a simple Express app with CSRF protection:
// Import necessary modules
const express = require('express');
const cookieParser = require('cookie-parser');
const csrf = require('csurf');
const app = express();
const port = 3000;
// Use cookieParser middleware to read cookies from incoming requests
app.use(cookieParser());
// Use express.urlencoded middleware to parse URL-encoded form data (from POST forms)
app.use(express.urlencoded({ extended: true }));
// Set up csurf middleware to enable CSRF protection using cookies
const csrfProtection = csrf({ cookie: true });
// Register the CSRF protection middleware globally
app.use(csrfProtection);
// Route to display an HTML form
app.get('/form', (req, res) => {
// req.csrfToken() generates a CSRF token for the current session
// Embed this token in a hidden form field
res.send(`
<form action="/submit" method="POST">
<input type="hidden" name="_csrf" value="${req.csrfToken()}">
<input type="text" name="name" placeholder="Enter name">
<button type="submit">Submit</button>
</form>
`);
});
// Route to handle form submission
app.post('/submit', (req, res) => {
// If CSRF token is valid, request reaches here
const name = req.body.name;
res.send(`Hello, ${name}. CSRF token validated.`);
});
// Start the server
app.listen(port, () => {
console.log(`App running at http://localhost:${port}`);
});
In this example, the form is returned directly as an HTML string using res.send()
, which is suitable for basic learning and quick testing. In real-world applications, you should use a template engine like EJS, Pug, or Handlebars to render HTML views and pass the CSRF token using res.render()
with <%= csrfToken %>
or equivalent syntax.
Step 3: Handling CSRF Errors
Use error-handling middleware to catch CSRF token mismatches:
app.use((err, req, res, next) => {
if (err.code === 'EBADCSRFTOKEN') {
// CSRF token mismatch
res.status(403).send('Invalid CSRF token.');
} else {
next(err);
}
});
Best Practices for CSRF Protection
- Use HTTPS: Prevent token sniffing during transmission.
- Avoid GET for state changes: Use POST, PUT, or DELETE for such requests.
- Regenerate tokens: Generate a new token for each session or form view.
Example with AJAX
To securely send AJAX requests, include the CSRF token in the request headers. If you're using EJS or another templating engine, pass the token from the server (e.g., using <%= csrfToken %>
) and inject it into your JavaScript code.
<script>
fetch('/submit', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'CSRF-Token': '<%= csrfToken %>' // Inject token using template engine
},
body: JSON.stringify({ name: 'Amit' })
})
.then(res => res.text())
.then(data => console.log(data));
</script>
Enable JSON Parsing in Express
app.use(express.json());
The csurf
middleware automatically checks the CSRF-Token
header.
Conclusion
In this tutorial, you learned what CSRF attacks are and why they pose a threat to authenticated web applications. You understood how CSRF protection works using tokens and how to implement it in an Express.js app using the csurf
middleware. You also learned how to handle CSRF errors, apply best practices like using HTTPS and proper HTTP methods, and how to secure AJAX requests by including the CSRF token in headers.