JS Tower
Node.jsМодуль 6: Безопасность

Защита от атак

XSS, CSRF, SQL Injection, Rate Limiting, CORS

Цель урока

В этом уроке ты научишься:

  • Защищаться от основных атак
  • Настраивать CORS
  • Ограничивать количество запросов

Helmet

Защита HTTP заголовков.

npm install helmet
const helmet = require('helmet');

app.use(helmet());

// Или с настройками
app.use(helmet({
  contentSecurityPolicy: {
    directives: {
      defaultSrc: ["'self'"],
      scriptSrc: ["'self'", "'unsafe-inline'"],
      styleSrc: ["'self'", "'unsafe-inline'"]
    }
  }
}));

XSS (Cross-Site Scripting)

Проблема

// Опасно: вставка пользовательского ввода
res.send(`<h1>Привет, ${req.query.name}</h1>`);
// ?name=<script>alert('XSS')</script>

Защита

npm install xss
const xss = require('xss');

// Очистка ввода
const safeName = xss(req.query.name);
res.send(`<h1>Привет, ${safeName}</h1>`);

// Или middleware
function sanitize(req, res, next) {
  if (req.body) {
    for (const key in req.body) {
      if (typeof req.body[key] === 'string') {
        req.body[key] = xss(req.body[key]);
      }
    }
  }
  next();
}

CSRF (Cross-Site Request Forgery)

Установка

npm install csurf

Настройка

const csrf = require('csurf');

const csrfProtection = csrf({ cookie: true });

app.use(cookieParser());
app.use(csrfProtection);

// Передаём токен клиенту
app.get('/form', (req, res) => {
  res.json({ csrfToken: req.csrfToken() });
});

// Клиент должен отправить токен в заголовке
// X-CSRF-Token: <token>

SQL/NoSQL Injection

Проблема

// Опасно!
const user = await User.findOne({ email: req.body.email });
// email: { "$gt": "" } — вернёт первого пользователя

Защита

// Валидация типов
const { email } = req.body;

if (typeof email !== 'string') {
  return res.status(400).json({ error: 'Invalid email' });
}

// Использование схем валидации (Joi, Zod)
const schema = Joi.object({
  email: Joi.string().email().required()
});

// Mongoose sanitize
npm install express-mongo-sanitize
const mongoSanitize = require('express-mongo-sanitize');

app.use(mongoSanitize());
// Удаляет $ и . из req.body, req.query, req.params

Rate Limiting

npm install express-rate-limit
const rateLimit = require('express-rate-limit');

// Общий лимит
const limiter = rateLimit({
  windowMs: 15 * 60 * 1000,  // 15 минут
  max: 100,                   // 100 запросов
  message: { error: 'Too many requests' }
});

app.use(limiter);

// Лимит для логина
const loginLimiter = rateLimit({
  windowMs: 60 * 60 * 1000,  // 1 час
  max: 5,                     // 5 попыток
  message: { error: 'Too many login attempts' }
});

app.post('/login', loginLimiter, loginHandler);

CORS

npm install cors
const cors = require('cors');

// Разрешить все origins (не для production!)
app.use(cors());

// С настройками
app.use(cors({
  origin: ['http://localhost:3000', 'https://myapp.com'],
  methods: ['GET', 'POST', 'PUT', 'DELETE'],
  allowedHeaders: ['Content-Type', 'Authorization'],
  credentials: true  // Для cookies
}));

// Динамический origin
app.use(cors({
  origin: (origin, callback) => {
    const allowedOrigins = ['http://localhost:3000'];
    
    if (!origin || allowedOrigins.includes(origin)) {
      callback(null, true);
    } else {
      callback(new Error('Not allowed by CORS'));
    }
  }
}));

Дополнительные меры

HPP (HTTP Parameter Pollution)

npm install hpp
const hpp = require('hpp');

app.use(hpp());
// ?sort=name&sort=email → ?sort=email (последний)

Compression

npm install compression
const compression = require('compression');

app.use(compression());

Чеклист безопасности

  • Используй HTTPS
  • Включи Helmet
  • Настрой CORS
  • Добавь Rate Limiting
  • Валидируй все входные данные
  • Используй параметризованные запросы
  • Храни секреты в переменных окружения
  • Логируй подозрительную активность
  • Регулярно обновляй зависимости

Практика

Задание: Безопасный сервер

Задача: Настрой базовую безопасность.

Решение:

const express = require('express');
const helmet = require('helmet');
const cors = require('cors');
const rateLimit = require('express-rate-limit');
const mongoSanitize = require('express-mongo-sanitize');

const app = express();

// Security middleware
app.use(helmet());
app.use(cors({ origin: process.env.FRONTEND_URL }));
app.use(mongoSanitize());
app.use(rateLimit({ windowMs: 15 * 60 * 1000, max: 100 }));

app.use(express.json({ limit: '10kb' }));

Проверь себя

  1. Что делает Helmet?
  2. Как защититься от XSS?
  3. Зачем нужен Rate Limiting?