API Security: 10 правил безопасности для разработчиков

← Вернуться к статьям

Введение

Безопасность API — это не опциональная функция, а критически важная основа любого веб-приложения. Уязвимости в API могут привести к утечкам данных, несанкционированному доступу и финансовым потерям.

В этом руководстве мы рассмотрим 10 критически важных правил безопасности для API, которые должен знать каждый разработчик. Эти правила помогут защитить ваше API от самых распространенных атак и уязвимостей.

⚠️ Критически важно:

Безопасность — это не одноразовая настройка, а непрерывный процесс. Регулярно проверяйте своё API на уязвимости, обновляйте зависимости и следите за новыми угрозами безопасности.

✅ Что вы узнаете:

📋 10 правил безопасности:

Правило 1: Всегда используйте HTTPS 🔒

1 Всегда используйте HTTPS

HTTPS (HyperText Transfer Protocol Secure) — это обязательный минимум безопасности. Без HTTPS все данные передаются в открытом виде и могут быть перехвачены злоумышленниками.

Почему это важно:

Как реализовать:

// Node.js + Express - принудительный HTTPS
const express = require('express');
const app = express();

// Редирект с HTTP на HTTPS
app.use((req, res, next) => {
  if (req.header('x-forwarded-proto') !== 'https') {
    res.redirect(`https://${req.header('host')}${req.url}`);
  } else {
    next();
  }
});

// Или используйте Helmet для security headers
const helmet = require('helmet');
app.use(helmet({
  hsts: {
    maxAge: 31536000,
    includeSubDomains: true,
    preload: true
  }
}));

⚠️ Важно:

  • Никогда не передавайте API keys или токены через HTTP
  • Используйте SSL/TLS сертификаты от доверенных CA
  • Настройте HSTS (HTTP Strict Transport Security)
  • Регулярно обновляйте SSL/TLS версии

Правило 2: Реализуйте аутентификацию 🔐

2 Реализуйте аутентификацию

Аутентификация — это процесс проверки личности пользователя. Без аутентификации любой может получить доступ к вашему API.

Методы аутентификации:

1. JWT (JSON Web Tokens)

JWT — популярный метод для stateless аутентификации. Токен содержит информацию о пользователе и подписан секретным ключом.

// Генерация JWT токена
const jwt = require('jsonwebtoken');

// Создание токена
const token = jwt.sign(
  { userId: user.id, email: user.email },
  process.env.JWT_SECRET,
  { expiresIn: '24h' }
);

// Верификация токена
app.use('/api', (req, res, next) => {
  const token = req.header('Authorization')?.replace('Bearer ', '');
  
  if (!token) {
    return res.status(401).json({ error: 'No token provided' });
  }
  
  try {
    const decoded = jwt.verify(token, process.env.JWT_SECRET);
    req.user = decoded;
    next();
  } catch (error) {
    return res.status(401).json({ error: 'Invalid token' });
  }
});
2. OAuth 2.0

OAuth 2.0 — стандарт для авторизации третьих сторон. Используется для интеграции с внешними сервисами.

// OAuth 2.0 flow
const { google } = require('googleapis');

const oauth2Client = new google.auth.OAuth2(
  process.env.CLIENT_ID,
  process.env.CLIENT_SECRET,
  process.env.REDIRECT_URI
);

// Получение токена
app.get('/auth/google', (req, res) => {
  const url = oauth2Client.generateAuthUrl({
    access_type: 'offline',
    scope: ['profile', 'email']
  });
  res.redirect(url);
});

// Callback
app.get('/auth/google/callback', async (req, res) => {
  const { code } = req.query;
  const { tokens } = await oauth2Client.getToken(code);
  // Сохраните tokens и создайте сессию
});
3. API Keys

API Keys — простой метод для идентификации клиентов. Используется для публичных API.

// API Key аутентификация
app.use('/api', (req, res, next) => {
  const apiKey = req.header('X-API-Key');
  
  if (!apiKey) {
    return res.status(401).json({ error: 'API key required' });
  }
  
  // Проверка API key в базе данных
  const client = await Client.findOne({ apiKey });
  if (!client || !client.active) {
    return res.status(401).json({ error: 'Invalid API key' });
  }
  
  req.client = client;
  next();
});

💡 Рекомендации:

  • JWT — для stateless аутентификации, короткий срок жизни токенов
  • OAuth 2.0 — для интеграции с внешними сервисами
  • API Keys — для публичных API, мониторинг использования
  • Всегда храните секретные ключи в переменных окружения
  • Используйте refresh tokens для обновления access tokens

Правило 3: Настройте авторизацию 🛡️

3 Настройте авторизацию

Авторизация определяет, что пользователь может делать после аутентификации. Это защищает от несанкционированного доступа к ресурсам.

Модели авторизации:

1. RBAC (Role-Based Access Control)
// RBAC - проверка ролей
const roles = {
  admin: ['read', 'write', 'delete', 'manage'],
  user: ['read', 'write'],
  guest: ['read']
};

function checkPermission(userRole, action) {
  return roles[userRole]?.includes(action) || false;
}

// Middleware для проверки прав
function requirePermission(action) {
  return (req, res, next) => {
    if (!checkPermission(req.user.role, action)) {
      return res.status(403).json({ error: 'Insufficient permissions' });
    }
    next();
  };
}

// Использование
app.delete('/api/users/:id', 
  authenticate,
  requirePermission('delete'),
  deleteUser
);
2. ACL (Access Control List)
// ACL - проверка доступа к ресурсу
async function canAccess(userId, resourceId, action) {
  const resource = await Resource.findById(resourceId);
  
  // Владелец может все
  if (resource.ownerId === userId) {
    return true;
  }
  
  // Проверка прав доступа
  const permission = await Permission.findOne({
    userId,
    resourceId,
    action
  });
  
  return !!permission;
}

// Middleware
async function checkAccess(action) {
  return async (req, res, next) => {
    const canAccessResource = await canAccess(
      req.user.id,
      req.params.id,
      action
    );
    
    if (!canAccessResource) {
      return res.status(403).json({ error: 'Access denied' });
    }
    next();
  };
}

⚠️ Принцип минимальных привилегий:

Пользователи должны иметь доступ только к тем ресурсам и операциям, которые им действительно нужны. Никогда не давайте больше прав, чем необходимо.

Правило 4: Добавьте rate limiting ⏱️

4 Добавьте rate limiting

Rate limiting ограничивает количество запросов от одного клиента за определенный период. Это защищает от DDoS атак, злоупотребления API и помогает контролировать использование ресурсов.

Стратегии rate limiting:

// Rate limiting с express-rate-limit
const rateLimit = require('express-rate-limit');

// Общий rate limit
const apiLimiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15 минут
  max: 100, // максимум 100 запросов
  message: 'Too many requests from this IP, please try again later.',
  standardHeaders: true,
  legacyHeaders: false,
});

app.use('/api/', apiLimiter);

// Строгий rate limit для авторизации
const authLimiter = rateLimit({
  windowMs: 15 * 60 * 1000,
  max: 5, // максимум 5 попыток входа
  skipSuccessfulRequests: true,
});

app.use('/api/auth/login', authLimiter);

// Rate limit на основе пользователя
const userLimiter = rateLimit({
  keyGenerator: (req) => req.user?.id || req.ip,
  windowMs: 60 * 1000,
  max: 30
});

app.use('/api/users/', userLimiter);

HTTP заголовки для rate limiting:

// Отправка информации о лимитах в заголовках
app.use((req, res, next) => {
  // X-RateLimit-Limit: общий лимит
  // X-RateLimit-Remaining: оставшиеся запросы
  // X-RateLimit-Reset: время сброса лимита
  res.set({
    'X-RateLimit-Limit': '100',
    'X-RateLimit-Remaining': '95',
    'X-RateLimit-Reset': new Date(Date.now() + 15 * 60 * 1000).toISOString()
  });
  next();
});

💡 Рекомендации:

  • Разные лимиты для разных endpoints (авторизация строже)
  • Используйте Redis для распределенного rate limiting
  • Предоставляйте информацию о лимитах в заголовках ответа
  • Используйте whitelist для доверенных клиентов

Правило 5: Валидируйте входные данные ✅

5 Валидируйте входные данные

Валидация входных данных — это первая линия защиты от атак. Всегда проверяйте и санитизируйте данные от клиента перед обработкой.

Что валидировать:

// Валидация с Joi
const Joi = require('joi');

const userSchema = Joi.object({
  name: Joi.string().min(2).max(50).required(),
  email: Joi.string().email().required(),
  age: Joi.number().integer().min(18).max(120),
  password: Joi.string().min(8).pattern(new RegExp('^[a-zA-Z0-9]{3,30}$'))
});

function validateUser(req, res, next) {
  const { error, value } = userSchema.validate(req.body);
  
  if (error) {
    return res.status(400).json({
      error: 'Validation error',
      details: error.details.map(d => d.message)
    });
  }
  
  req.validated = value;
  next();
}

app.post('/api/users', validateUser, createUser);

// Валидация с express-validator
const { body, validationResult } = require('express-validator');

app.post('/api/users', [
  body('email').isEmail().normalizeEmail(),
  body('name').trim().isLength({ min: 2, max: 50 }),
  body('age').isInt({ min: 18, max: 120 }),
  body('password').isLength({ min: 8 }).matches(/^[a-zA-Z0-9]{8,}$/)
], (req, res) => {
  const errors = validationResult(req);
  if (!errors.isEmpty()) {
    return res.status(400).json({ errors: errors.array() });
  }
  // Обработка валидных данных
});

❌ Никогда не доверяйте клиенту:

  • Всегда валидируйте данные на сервере, даже если есть валидация на клиенте
  • Санитизируйте HTML перед отображением
  • Проверяйте файлы перед загрузкой (тип, размер)
  • Ограничивайте размер payload для защиты от DoS

Правило 6: Защитите от SQL injection 🛡️

6 Защитите от SQL injection

SQL injection — это атака, при которой злоумышленник вставляет вредоносный SQL код в запросы к базе данных. Это одна из самых опасных уязвимостей.

Как защититься:

1. Используйте Prepared Statements
// ❌ НЕПРАВИЛЬНО - уязвимо к SQL injection
app.get('/api/users', (req, res) => {
  const query = `SELECT * FROM users WHERE email = '${req.query.email}'`;
  db.query(query, (err, results) => {
    res.json(results);
  });
});

// ✅ ПРАВИЛЬНО - используйте prepared statements
app.get('/api/users', (req, res) => {
  const query = 'SELECT * FROM users WHERE email = ?';
  db.query(query, [req.query.email], (err, results) => {
    res.json(results);
  });
});
2. Используйте ORM
// Sequelize ORM автоматически защищает от SQL injection
const User = require('./models/User');

app.get('/api/users', async (req, res) => {
  const users = await User.findAll({
    where: {
      email: req.query.email
    }
  });
  res.json(users);
});

// Mongoose для MongoDB
const User = require('./models/User');

app.get('/api/users', async (req, res) => {
  const users = await User.find({ email: req.query.email });
  res.json(users);
});

⚠️ Дополнительные меры защиты:

  • Ограничьте права доступа к базе данных (только необходимые операции)
  • Используйте параметризованные запросы всегда
  • Экранируйте специальные символы
  • Регулярно обновляйте СУБД и драйверы

Правило 7: Защитите от XSS 🚫

7 Защитите от XSS

XSS (Cross-Site Scripting) — это атака, при которой злоумышленник внедряет вредоносный JavaScript код в веб-приложение. Это может привести к краже сессий, cookie и данных.

Типы XSS атак:

Как защититься:

// Санитизация HTML с DOMPurify
const DOMPurify = require('dompurify');
const { JSDOM } = require('jsdom');

const window = new JSDOM('').window;
const purify = DOMPurify(window);

// Санитизация перед сохранением
app.post('/api/posts', (req, res) => {
  const sanitizedContent = purify.sanitize(req.body.content);
  // Сохраните sanitizedContent в базу данных
});

// Экранирование HTML с helmet
const helmet = require('helmet');
app.use(helmet({
  contentSecurityPolicy: {
    directives: {
      defaultSrc: ["'self'"],
      scriptSrc: ["'self'"],
      styleSrc: ["'self'", "'unsafe-inline'"],
      imgSrc: ["'self'", "https:"],
    },
  },
}));

// Экранирование для шаблонов
const escapeHtml = (text) => {
  const map = {
    '&': '&',
    '<': '<',
    '>': '>',
    '"': '"',
    "'": '''
  };
  return text.replace(/[&<>"']/g, m => map[m]);
};

💡 Content Security Policy (CSP):

CSP позволяет контролировать, какие ресурсы могут загружаться на странице. Это эффективная защита от XSS атак.

Правило 8: Правильно настройте CORS 🌐

8 Правильно настройте CORS

CORS (Cross-Origin Resource Sharing) определяет, какие домены могут делать запросы к вашему API. Неправильная настройка CORS может открыть ваше API для атак.

Как настроить CORS безопасно:

// ❌ НЕПРАВИЛЬНО - открывает доступ всем
const cors = require('cors');
app.use(cors()); // Разрешает все домены!

// ✅ ПРАВИЛЬНО - белый список доменов
const corsOptions = {
  origin: (origin, callback) => {
    const whitelist = [
      'https://example.com',
      'https://app.example.com'
    ];
    
    // Разрешить запросы без origin (мобильные приложения, Postman)
    if (!origin || whitelist.includes(origin)) {
      callback(null, true);
    } else {
      callback(new Error('Not allowed by CORS'));
    }
  },
  credentials: true, // Разрешить cookies
  optionsSuccessStatus: 200,
  methods: ['GET', 'POST', 'PUT', 'DELETE'],
  allowedHeaders: ['Content-Type', 'Authorization']
};

app.use(cors(corsOptions));

// Или для конкретного endpoint
app.get('/api/data', cors({
  origin: 'https://example.com'
}), (req, res) => {
  res.json({ data: 'secret data' });
});

⚠️ Важные правила:

  • Никогда не используйте Access-Control-Allow-Origin: * с credentials
  • Указывайте конкретные домены вместо wildcard
  • Ограничьте разрешенные методы и заголовки
  • Для production используйте строгий белый список

Правило 9: Используйте безопасные заголовки 🛡️

9 Используйте безопасные заголовки

Security headers помогают защитить ваше приложение от различных атак. Используйте библиотеку Helmet для автоматической настройки заголовков.

// Настройка Helmet с рекомендуемыми настройками
const helmet = require('helmet');
app.use(helmet());

// Или настройка вручную
app.use(helmet({
  contentSecurityPolicy: {
    directives: {
      defaultSrc: ["'self'"],
      scriptSrc: ["'self'", "'unsafe-inline'"],
      styleSrc: ["'self'", "'unsafe-inline'"],
    },
  },
  hsts: {
    maxAge: 31536000,
    includeSubDomains: true,
    preload: true
  },
  noSniff: true, // Защита от MIME sniffing
  xssFilter: true, // Встроенная защита от XSS в браузере
  frameguard: {
    action: 'deny' // Защита от clickjacking
  }
}));

// Дополнительные заголовки вручную
app.use((req, res, next) => {
  res.setHeader('X-Content-Type-Options', 'nosniff');
  res.setHeader('X-Frame-Options', 'DENY');
  res.setHeader('X-XSS-Protection', '1; mode=block');
  res.setHeader('Referrer-Policy', 'strict-origin-when-cross-origin');
  res.setHeader('Permissions-Policy', 'geolocation=(), microphone=(), camera=()');
  next();
});

Важные заголовки:

Заголовок Назначение
Content-Security-Policy Контроль загружаемых ресурсов, защита от XSS
X-Content-Type-Options Защита от MIME sniffing
X-Frame-Options Защита от clickjacking
Strict-Transport-Security Принудительное использование HTTPS
X-XSS-Protection Встроенная защита от XSS в браузере

Правило 10: Логируйте и мониторьте 📊

10 Логируйте и мониторьте

Логирование и мониторинг помогают обнаружить атаки и проблемы безопасности в реальном времени. Без логов невозможно расследовать инциденты безопасности.

Что логировать:

// Настройка логирования с Winston
const winston = require('winston');

const logger = winston.createLogger({
  level: 'info',
  format: winston.format.json(),
  transports: [
    new winston.transports.File({ filename: 'error.log', level: 'error' }),
    new winston.transports.File({ filename: 'combined.log' })
  ]
});

// Middleware для логирования запросов
app.use((req, res, next) => {
  const start = Date.now();
  
  res.on('finish', () => {
    const duration = Date.now() - start;
    logger.info({
      method: req.method,
      url: req.url,
      status: res.statusCode,
      duration,
      ip: req.ip,
      userAgent: req.get('user-agent')
    });
    
    // Логирование подозрительной активности
    if (res.statusCode === 401 || res.statusCode === 403) {
      logger.warn({
        type: 'security',
        message: 'Unauthorized access attempt',
        ip: req.ip,
        url: req.url
      });
    }
  });
  
  next();
});

// Логирование критичных операций
app.delete('/api/users/:id', authenticate, async (req, res) => {
  logger.warn({
    type: 'audit',
    action: 'user_deleted',
    userId: req.user.id,
    targetUserId: req.params.id,
    ip: req.ip,
    timestamp: new Date()
  });
  
  await deleteUser(req.params.id);
  res.json({ success: true });
});

Мониторинг безопасности:

💡 Инструменты для мониторинга:

  • Prometheus + Grafana: Метрики и визуализация
  • ELK Stack: Централизованное логирование
  • Datadog / New Relic: APM и мониторинг
  • Sentry: Отслеживание ошибок

Дополнительные рекомендации 🔒

✅ Чек-лист безопасности API:

Заключение

Безопасность API — это не одноразовая настройка, а непрерывный процесс. Эти 10 правил должны стать основой безопасности любого API. Однако помните:

💡 Помните:

⚠️ Никогда не пренебрегайте:

Самые серьезные уязвимости часто возникают из-за простых ошибок: недостаточной валидации, слабых паролей, неправильной настройки CORS. Всегда следуйте принципу "недоверия к входным данным" и "принципу минимальных привилегий".

Создайте безопасный Mock API за 2 минуты

LightBox API поддерживает все стандарты безопасности: HTTPS, CORS, rate limiting. Создайте безопасный Mock API за 2 минуты и начните разработку без рисков.

Попробовать бесплатно →
← Вернуться к статьям