csrf-protector
v1.0.0
Published
Robust CSRF protection for Node.js applications with support for stateful and stateless approaches
Maintainers
Readme
secure-csrf
A robust, modern CSRF protection library for Node.js applications with zero dependencies. The package supports both stateful and stateless approaches to CSRF protection, making it flexible for various application architectures.
Features
- 🔐 Strong security: Uses cryptographically secure token generation
- 🧱 Zero dependencies: No external packages required
- 🔄 Multiple modes: Support for both stateful (session-based) and stateless (cookie-based) approaches
- 🔄 Secret rotation: Advanced protection with automatic secret rotation
- 🚀 TypeScript support: Full TypeScript definitions included
- 📦 Framework support: First-class Express.js integration with extensibility for other frameworks
- 📝 Well-documented: Comprehensive documentation and examples
Installation
npm install secure-csrfor
yarn add secure-csrfQuick Start
Express.js Integration
const express = require('express');
const session = require('express-session');
const { expressMiddleware } = require('secure-csrf');
const app = express();
// Setup session middleware (required for stateful mode)
app.use(session({
secret: 'your-session-secret',
resave: false,
saveUninitialized: false,
cookie: { secure: process.env.NODE_ENV === 'production' }
}));
// Parse request bodies
app.use(express.urlencoded({ extended: false }));
app.use(express.json());
// Add CSRF protection
app.use(expressMiddleware());
// Routes
app.get('/', (req, res) => {
// Pass the CSRF token to your template
res.render('form', { csrfToken: req.csrfToken() });
});
app.post('/submit', (req, res) => {
// The request has already been validated by the CSRF middleware
res.send('Form submitted successfully!');
});
app.listen(3000, () => {
console.log('Server running on port 3000');
});In your HTML form
<form action="/submit" method="post">
<input type="hidden" name="_csrf" value="<%= csrfToken %>">
<input type="text" name="name" placeholder="Your name">
<button type="submit">Submit</button>
</form>With AJAX requests
// Get the CSRF token from a meta tag, for example
const csrfToken = document.querySelector('meta[name="csrf-token"]').getAttribute('content');
fetch('/api/data', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-Token': csrfToken // Send the token in a header
},
body: JSON.stringify({ key: 'value' })
})
.then(response => response.json())
.then(data => console.log(data));Usage Modes
Stateful Mode (Default)
In stateful mode, the CSRF secret is stored in the user's session. This is the most secure approach but requires a session management system.
const { expressMiddleware } = require('secure-csrf');
app.use(expressMiddleware({
mode: 'stateful', // This is the default
sessionKey: 'csrfSecret' // The key used to store the secret in the session
}));Stateless Mode
In stateless mode, the CSRF secret is stored in a cookie. This approach doesn't require server-side session storage but provides slightly less security.
const { expressMiddleware } = require('secure-csrf');
app.use(expressMiddleware({
mode: 'stateless',
cookie: {
secure: true, // Only send over HTTPS
httpOnly: true, // Not accessible via JavaScript
sameSite: 'strict', // Stricter CSRF protection
maxAge: 3600 // 1 hour in seconds
}
}));Security Considerations
Stateful vs. Stateless Approaches
Stateful (Synchronizer Token Pattern):
- Token is generated on the server and stored in the session
- Client receives a copy of the token to include in future requests
- Provides stronger security as the secret never leaves the server
- Requires session storage on the server
Stateless (Double Submit Cookie Pattern):
- Secret is stored in a cookie
- Token is generated using this secret and sent to the client
- On subsequent requests, both the cookie and token are validated
- No server-side session storage required
- Slightly less secure than the stateful approach
Secret Rotation
For advanced security, you can enable automatic secret rotation:
const { expressMiddleware } = require('secure-csrf');
app.use(expressMiddleware({
secretRotation: {
enabled: true,
interval: 3600000, // Rotate secrets every hour (in milliseconds)
maxSecrets: 2 // Keep 2 most recent secrets (to validate existing tokens)
}
}));Security Best Practices
Use HTTPS: Always use HTTPS in production to protect cookies and headers from eavesdropping.
Set Secure Cookies: Use the
secureandhttpOnlyflags on cookies to enhance security.Use SameSite Cookies: The
sameSiteattribute helps prevent CSRF in modern browsers.Implement Secret Rotation: Regularly rotating secrets limits the window of opportunity for attackers.
Protect All Sensitive Actions: Apply CSRF protection to all state-changing operations, not just forms.
API Reference
Core Module
SecureCsrf
The main class providing CSRF protection functionality.
import { SecureCsrf, CsrfMode } from 'secure-csrf';
const csrf = new SecureCsrf({
mode: CsrfMode.STATEFUL,
tokenLength: 32,
// other options...
});Methods:
generateSecret(length?: number): string- Generate a secure random secretgenerateSecretAsync(length?: number): Promise<string>- Generate a secret asynchronouslycreateToken(secret?: string, data?: string): CsrfToken- Create a new CSRF tokenverifyToken(token: string, secret?: string): boolean- Verify a CSRF tokenrotateSecret(newSecret?: string): void- Rotate the secretdestroy(): void- Clean up resources (e.g., secret rotation timer)
Express Middleware
import { expressMiddleware } from 'secure-csrf';
app.use(expressMiddleware({
// options...
}));Options:
mode: CSRF protection mode ('stateful'or'stateless')cookieName: Name of the cookie (default:'_csrf')headerName: Name of the header (default:'x-csrf-token')fieldName: Name of the form field (default:'_csrf')sessionKey: Session key for storing the secret (default:'csrfSecret')ignoredRoutes: Routes to ignore (default:[])ignoredMethods: HTTP methods to ignore (default:['GET', 'HEAD', 'OPTIONS'])cookie: Cookie options for stateless modesecretRotation: Secret rotation settingsgetToken: Custom function to extract token from requestonError: Custom error handler
Advanced Usage
Custom Token Extraction
app.use(expressMiddleware({
getToken: (req) => {
// Custom logic to extract token from request
return req.headers['my-custom-header'] || req.body.myCustomField;
}
}));Custom Error Handling
app.use(expressMiddleware({
onError: (err, req, res, next) => {
// Custom error handling
res.status(403).json({
error: 'CSRF validation failed',
message: err.message,
code: 'CSRF_ERROR'
});
}
}));Ignoring Specific Routes
app.use(expressMiddleware({
ignoredRoutes: [
'/webhook', // Exact match
/^\/api\/public\// // RegExp pattern
]
}));Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
License
This project is licensed under the MIT License - see the LICENSE file for details.
Developed with ❤️ for the Node.js community.
