Rate Limiting в JavaScript (Node.js): Защита API с помощью express-rate-limit
В современном веб-разработке защита серверных приложений от чрезмерной нагрузки и злонамеренных атак является критически важной задачей. Одним из наиболее эффективных механизмов защиты является rate limiting (ограничение частоты запросов). Этот подход позволяет контролировать количество запросов, которые клиент (IP-адрес, пользователь или сессия) может отправить на сервер за определенный промежуток времени. В экосистеме Node.js и Express наиболее популярной и надежной библиотекой для реализации rate limiting является express-rate-limit.
Что такое rate limiting и зачем он нужен?
Rate limiting — это техника, используемая для управления трафиком в сети. Она предотвращает злоупотребление ресурсами сервера, защищая от:
- DoS/DDoS-атак — массовых запросов с целью вывести сервер из строя.
- Brute-force атак — перебора паролей или токенов доступа.
- Web scraping — автоматического сбора данных с сайта.
- Чрезмерного использования API — когда один клиент потребляет больше ресурсов, чем положено.
Без rate limiting ваше приложение может стать уязвимым и нестабильным, особенно при росте пользовательской базы.
Библиотека express-rate-limit
express-rate-limit — это middleware для Express.js, который предоставляет простой и гибкий способ ограничения запросов. Она встраивается в цепочку обработки запросов и автоматически отслеживает количество обращений от каждого клиента. Библиотека поддерживает различные хранилища (в памяти, Redis, MongoDB и другие), что делает ее масштабируемой для распределенных систем.
Установка
Для начала работы установите библиотеку через npm:
npm install express-rate-limit
Базовое использование
Самый простой пример — ограничение всех маршрутов приложения до 100 запросов в час с одного IP-адреса:
const rateLimit = require('express-rate-limit');
const express = require('express');
const app = express();
// Создаем лимитер
const limiter = rateLimit({
windowMs: 60 * 60 * 1000, // 1 час
max: 100, // максимум 100 запросов
message: 'Слишком много запросов с вашего IP. Пожалуйста, попробуйте позже.',
standardHeaders: true, // Возвращает стандартные заголовки RateLimit-*
legacyHeaders: false, // Отключает заголовки X-RateLimit-*
});
// Применяем ко всем маршрутам
app.use(limiter);
app.get('/', (req, res) => {
res.send('Главная страница');
});
app.listen(3000, () => {
console.log('Сервер запущен на порту 3000');
});
Настройка для разных маршрутов
Часто требуется разное ограничение для различных эндпоинтов. Например, для маршрута входа в систему можно установить более строгий лимит:
const rateLimit = require('express-rate-limit');
// Строгий лимит для /api/login
const loginLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 минут
max: 5, // 5 попыток входа
message: {
error: 'Слишком много попыток входа. Попробуйте через 15 минут.',
retryAfter: 15
},
keyGenerator: (req) => {
// Используем IP + имя пользователя для более точной блокировки
return req.ip + req.body.username;
}
});
app.post('/api/login', loginLimiter, (req, res) => {
// Логика аутентификации
res.send('Вход выполнен');
});
// Мягкий лимит для публичного API
const apiLimiter = rateLimit({
windowMs: 60 * 1000, // 1 минута
max: 30, // 30 запросов в минуту
message: 'Превышен лимит запросов к API.'
});
app.use('/api/', apiLimiter);
Расширенные возможности
Кастомные хранилища
По умолчанию данные хранятся в памяти процесса. Для продакшена рекомендуется использовать Redis или MongoDB. Пример с Redis через rate-limit-redis:
const RedisStore = require('rate-limit-redis');
const Redis = require('ioredis');
const redisClient = new Redis();
const limiter = rateLimit({
store: new RedisStore({
sendCommand: (...args) => redisClient.call(...args),
}),
windowMs: 60 * 60 * 1000,
max: 100,
});
Пропуск определенных запросов
Иногда нужно исключить из лимитирования некоторые запросы (например, от доверенных IP):
const limiter = rateLimit({
windowMs: 60 * 60 * 1000,