Основы безопасности
Сайт в интернете сразу начинают проверять боты: подбирают пароли, отправляют странные данные в формы, ищут известные уязвимости. Несколько простых мер закрывают большую часть типичных атак. Многие из них уже встречались в курсе, здесь они собраны вместе.
Защитные заголовки: helmet
Браузеры умеют защищать посетителей, если сервер попросит об этом заголовками ответа. Пакет helmet выставляет набор таких заголовков одной строкой:
npm install helmet// src/app.js
import helmet from 'helmet';
const app = express();
app.use(helmet());Подключайте его первым. Откройте инструменты разработчика, вкладку «Сеть», и выберите любой запрос: в ответе появятся заголовки Content-Security-Policy, X-Content-Type-Options, Strict-Transport-Security и другие, а заголовок X-Powered-By: Express, по которому легко узнать сервер, исчезнет.
Заголовок Content-Security-Policy по умолчанию запрещает скрипты и стили с чужих доменов. Если блог подключает, например, шрифты с внешнего сайта, этот домен нужно разрешить в настройках helmet.
Ограничение частоты запросов
Бот может проверять тысячи паролей в минуту. Пакет express-rate-limit ограничивает число запросов с одного IP-адреса:
npm install express-rate-limit// src/routes/auth.js
import { rateLimit } from 'express-rate-limit';
const loginLimiter = rateLimit({
windowMs: 15 * 60 * 1000,
limit: 10,
message: 'Слишком много попыток входа, попробуйте через 15 минут',
});
router.post('/login', loginLimiter, authController.login);С одного адреса можно сделать 10 попыток входа за 15 минут, одиннадцатая получит ответ с кодом 429 и этим сообщением. Общий, более мягкий лимит можно повесить и на весь API: app.use('/api', apiLimiter).
Проверка входных данных
Всё, что пришло от клиента, — параметры, строка запроса, тело, заголовки, файлы — может быть чем угодно. Правила из прошлых уроков:
| Угроза | Защита |
|---|---|
| Неверные или лишние поля | Схема Zod и safeParse() |
| SQL-инъекция | Знаки ? в запросах, а не шаблонные строки |
| XSS в шаблонах | Вывод через <%=, а не <%- |
| Опасные файлы | Фильтр типа, лимит размера, имя от сервера |
| Подбор паролей | bcrypt и ограничение частоты |
Безопасные cookie
Для cookie сессии на рабочем сервере включите дополнительные флаги:
// src/app.js
app.use(session({
secret: config.sessionSecret,
resave: false,
saveUninitialized: false,
cookie: { httpOnly: true, secure: config.isProduction, sameSite: 'lax' },
}));secure отправляет cookie только по HTTPS, а sameSite: 'lax' не даёт чужим сайтам отправлять формы от имени вошедшего пользователя.
Обновления
Уязвимости находят и в пакетах. Команда npm audit проверяет зависимости по базе известных проблем, а npm outdated показывает устаревшие версии. Запускайте их регулярно.
Частая ошибка
Не показывайте посетителям текст ошибки и стек вызовов: res.status(500).send(error.stack) раскроет пути к файлам, версии пакетов и куски кода. Подробности пишите в журнал через console.error(), а посетителю отвечайте общим «Ошибка сервера».