Preventing Cross-Site Request Forgery (CSRF) in a stateless Node.js RESTful API using JSON Web Tokens (JWT) requires a different approach than traditional session-based applications. While JWTs provide a way to authenticate users, they do not inherently protect against CSRF attacks. Here are some effective strategies you can implement to safeguard your API:
1. Use Same-Origin Policy and CORS
Ensure that your API only accepts requests from trusted origins. You can configure Cross-Origin Resource Sharing (CORS) in your Express app to restrict access:
const cors = require('cors');
const corsOptions = {
origin: 'https://your-frontend-app.com',
methods: 'POST, GET, PUT, DELETE',
allowedHeaders: ['Content-Type', 'Authorization'],
};
app.use(cors(corsOptions));
This setup will only allow requests from your specified front-end origin.
2. Use Anti-CSRF Tokens
• Although JWTs are used for authentication, incorporating an anti-CSRF token adds another layer of protection. Here's how you can do it:
• Generate a CSRF Token: When a user authenticates (e.g., logs in), generate a CSRF token and send it to the client.
Send CSRF Token in Headers: The client should include this CSRF token in every state-changing request (like POST, PUT, DELETE) as a custom header.
// Client-side (e.g., using Fetch API)
fetch('/api/resource', {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'X-CSRF-Token': csrfToken // Include CSRF token
},
body: JSON.stringify(data),
});
• Validate the CSRF Token on the Server: In your API, create middleware to validate the CSRF token with each request.
const csrfProtection = (req, res, next) => {
const token = req.headers['x-csrf-token'];
if (!token || token !== expectedCsrfToken) {
return res.status(403).json({ message: 'Invalid CSRF token' });
}
next();
};
app.use(csrfProtection);
3. Limit HTTP Methods
Ensure that your API only accepts specific HTTP methods for sensitive actions. For example, use POST for creating resources, and avoid using GET for actions that modify data.
4. Validate the Origin Header
You can check the Origin header in your requests to ensure they are coming from a trusted source. If the origin is not recognized, deny the request.
const originCheck = (req, res, next) => {
const allowedOrigins = ['https://your-frontend-app.com'];
const origin = req.headers.origin;
if (allowedOrigins.includes(origin)) {
return next();
}
return res.status(403).json({ message: 'Invalid Origin' });
};
app.use(originCheck);
5. Use Secure Cookies for JWT Storage
If you're using cookies to store your JWTs, ensure they are secure and HTTP-only. This reduces the chances of malicious scripts accessing the token.
res.cookie('token', jwtToken, {
httpOnly: true,
secure: true, // Only send over HTTPS
sameSite: 'Strict', // Helps to mitigate CSRF
})