Kodokon kodokon.com

Защита API в продакшене

Укрепи Node-API в продакшене: правильный CORS, ограничение частоты запросов, заголовки безопасности и защита от главных рисков OWASP API.

11 мин · 3 вопросов

Открыть этот урок в Kodokon

OWASP ведёт отдельный топ-10, посвящённый API. Три доминирующих риска: BOLA (Broken Object Level Authorization - доступ к чужому объекту через подмену id), Broken Authentication (плохо проверенные токены, слабые секреты) и Unrestricted Resource Consumption (нет ограничений на частоту, размер или время). Обрати внимание: ни один из трёх не решается установкой библиотеки - это свойства твоего дизайна. Начнём со сквозных механизмов: CORS, ограничение частоты запросов и заголовки.

JAVASCRIPT
import { createServer } from "node:http";

const allowed = new Set(["https://app.example.com"]);

const server = createServer((req, res) => {
  const origin = req.headers.origin;
  if (origin && allowed.has(origin)) {
    res.setHeader(
      "Access-Control-Allow-Origin",
      origin,
    );
    res.setHeader("Vary", "Origin");
  }
  if (req.method === "OPTIONS") {
    res.setHeader(
      "Access-Control-Allow-Methods",
      "GET,POST,PUT,DELETE",
    );
    res.setHeader(
      "Access-Control-Allow-Headers",
      "content-type,authorization",
    );
    res.setHeader("Access-Control-Max-Age", "86400");
    res.writeHead(204).end();
    return;
  }
  res.setHeader("Content-Type", "application/json");
  res.end(JSON.stringify({ ok: true }));
});

server.listen(3000);
CORS вручную: белый список и preflight.

Чётко пойми, чем CORS не является: это не защита сервера. curl или сторонний бэкенд полностью игнорируют эти заголовки; их соблюдает только браузер, чтобы защитить своих пользователей. Три экспертных правила: возвращай точный origin из белого списка (никогда не отражай заголовок Origin вслепую), добавляй Vary: Origin, чтобы не отравить промежуточные кеши, и помни, что подстановочный знак * отвергается браузерами, как только запрос несёт куки (credentials).

JAVASCRIPT
const WINDOW_MS = 60_000;
const LIMIT = 100;
const hits = new Map();

function check(ip) {
  const now = Date.now();
  const entry = hits.get(ip);
  if (!entry || now - entry.start >= WINDOW_MS) {
    hits.set(ip, { start: now, count: 1 });
    return { ok: true, remaining: LIMIT - 1 };
  }
  entry.count += 1;
  const remaining = Math.max(LIMIT - entry.count, 0);
  return { ok: entry.count <= LIMIT, remaining };
}

console.log(check("203.0.113.7"));
Фиксированное окно в памяти: верни 429, если ok равно false.

Заголовки безопасности стоят нескольких строк и блокируют целые классы атак: HSTS заставляет использовать HTTPS при будущих визитах, X-Content-Type-Options: nosniff не даёт браузеру угадывать MIME-тип, а строгий CSP (default-src 'none') обезвреживает эксплуатацию ответа API, по ошибке отрисованного в браузере. Также убирай любой заголовок, раскрывающий твой стек: Express по умолчанию добавляет X-Powered-By - отключи его через app.disable('x-powered-by').

JAVASCRIPT
function setSecurityHeaders(res) {
  res.setHeader(
    "Strict-Transport-Security",
    "max-age=63072000; includeSubDomains",
  );
  res.setHeader("X-Content-Type-Options", "nosniff");
  res.setHeader(
    "Content-Security-Policy",
    "default-src 'none'; frame-ancestors 'none'",
  );
  res.setHeader("Cache-Control", "no-store");
}

export { setSecurityHeaders };
Вызывать при каждом ответе API.

Проверка знаний

Убедись, что запомнил ключевые моменты этого урока.

  1. Что на самом деле защищает механизм CORS?
    • Сервер от автоматических запросов (curl, скрипты)
    • Пользователя браузера от несанкционированного чтения между источниками
    • API от атак типа «отказ в обслуживании»
  2. Почему Access-Control-Allow-Origin: * несовместим с запросами, отправляющими куки?
    • Браузеры отвергают такую комбинацию, чтобы не открывать аутентифицированные сессии любому сайту
    • Сервер автоматически возвращает ошибку 500
    • Подстановочный знак отключает куки на стороне сервера
  3. Аутентифицированный API отдаёт GET /invoices/42 пользователю, которому этот счёт не принадлежит. Какой это риск OWASP API?
    • Injection
    • Broken Object Level Authorization (BOLA)
    • Security Misconfiguration
    • Server-Side Request Forgery