Encodera

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.

Оглавление

Основы безопасности

Сайт в интернете сразу начинают проверять боты: подбирают пароли, отправляют странные данные в формы, ищут известные уязвимости. Несколько простых мер закрывают большую часть типичных атак. Многие из них уже встречались в курсе, здесь они собраны вместе.

Защитные заголовки: 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 сессии на рабочем сервере включите дополнительные флаги:

// 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(), а посетителю отвечайте общим «Ошибка сервера».

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.