How to Implement Rate Limiting and CAPTCHA in Express.js
In today’s web environment, ensuring that your APIs and applications are both secure and responsive is more critical than ever. Rate limiting is a key strategy to protect your application from abuse, such as brute force attacks and DDoS, by controlling the number of requests a user can make in a given timeframe. Combining rate limiting with CAPTCHAs adds an additional layer of security, ensuring that only genuine users can access your services. This guide will provide a step-by-step approach to implementing these techniques using Express.js.
What is Rate Limiting?
Rate limiting restricts the number of requests a client can make to a server within a specified timeframe. It helps maintain system stability, prevent abuse, and ensure fair access to resources. Here are the main benefits:
Preventing Overload: Controls the number of requests to keep servers responsive and available.
Mitigating Abuse: Protects against brute force attacks and spam by limiting repeated actions.
Managing High Traffic: Ensures fair distribution of access during peak times, such as sales events.
DDoS Protection: Helps identify and block malicious traffic aimed at overwhelming the server.
Cost Optimization: In cloud environments, limits unnecessary scaling and reduces costs.
Real-World Example: Implementing Rate Limiting in Express.js
To demonstrate rate limiting, let's protect a login endpoint from brute force attacks by limiting the number of login attempts.
Setting Up Your Project
First, create a new Node.js project and install the necessary packages:
npm init -y
npm install express express-rate-limit
Configuring and Applying Rate Limiting
The following code snippet demonstrates how to set up basic rate limiting for a login route using Express.js:
const express = require('express');
const rateLimit = require('express-rate-limit');
const app = express();
const PORT = 3000;
app.use(express.json());
// Rate limiting configuration for login endpoint
const loginLimiter = rateLimit({
windowMs: 60 * 60 * 1000, // 1 hour window
max: 5, // Limit each IP to 5 requests per windowMs
message: 'Too many login attempts from this IP, please try again after an hour.'
});
// Apply rate limiting to the login route
app.post('/login', loginLimiter, (req, res) => {
// Simulate login logic
res.send('Login attempt');
});
app.listen(PORT, () => {
console.log(`Server running on http://localhost:${PORT}`);
});
Explanation:
- The
/login
endpoint uses theloginLimiter
middleware, restricting each IP to five login attempts per hour. This helps prevent brute force attacks by limiting how often login attempts can be made from the same IP address.
Enhancing Security with CAPTCHAs
While rate limiting is effective, it can be bypassed by attackers using multiple IPs. Implementing CAPTCHAs helps ensure that requests are coming from real users, adding an extra layer of security. Below, we'll integrate Cloudflare Turnstile, a modern CAPTCHA solution.
Setting Up Cloudflare Turnstile
Register and Get API Keys: Sign up on the Cloudflare Turnstile website to get your site key and secret key.
Install Required Packages:
npm install express-formidable axios
express-formidable
handles form data, whileaxios
makes HTTP requests.
Implementing CAPTCHA Verification
Add CAPTCHA verification to your login endpoint with the following code:
const formidable = require('express-formidable');
const axios = require('axios');
app.use(formidable()); // Middleware to handle form data
// Endpoint to handle login with CAPTCHA verification
app.post('/login', loginLimiter, async (req, res) => {
const { token, username, password } = req.fields;
if (!token) {
return res.status(400).send('CAPTCHA token is required');
}
// Verify CAPTCHA token with Cloudflare Turnstile
try {
const response = await axios.post('https://challenges.cloudflare.com/turnstile/v0/siteverify', null, {
params: {
secret: 'YOUR_SECRET_KEY',
response: token
}
});
if (!response.data.success) {
return res.status(403).send('CAPTCHA verification failed');
}
// Proceed with login logic if CAPTCHA is verified
if (username === 'admin' && password === 'password') {
res.send('Login successful');
} else {
res.status(401).send('Invalid credentials');
}
} catch (error) {
console.error('Error verifying CAPTCHA:', error);
res.status(500).send('Internal server error');
}
});
Explanation:
- Each login attempt now requires a CAPTCHA token. This token is verified using Cloudflare’s API. If the verification fails, the login attempt is blocked, ensuring only human users can log in.
Conclusion
Combining rate limiting and CAPTCHAs is essential for securing web applications. These techniques help protect against abuse, ensure fair resource allocation, and maintain service stability. By implementing these strategies, developers can build secure and responsive applications that provide a seamless experience for legitimate users.
Subscribe to my newsletter
Read articles from Swami Buddha Chaitanya directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by