Введение
Безопасность API — это не опциональная функция, а критически важная основа любого веб-приложения. Уязвимости в API могут привести к утечкам данных, несанкционированному доступу и финансовым потерям.
В этом руководстве мы рассмотрим 10 критически важных правил безопасности для API, которые должен знать каждый разработчик. Эти правила помогут защитить ваше API от самых распространенных атак и уязвимостей.
⚠️ Критически важно:
Безопасность — это не одноразовая настройка, а непрерывный процесс. Регулярно проверяйте своё API на уязвимости, обновляйте зависимости и следите за новыми угрозами безопасности.
✅ Что вы узнаете:
- ✅ 10 критически важных правил безопасности для API
- ✅ Аутентификация и авторизация (JWT, OAuth, API keys)
- ✅ Защита от SQL injection, XSS и других атак
- ✅ HTTPS и безопасные заголовки
- ✅ Rate limiting и защита от DDoS
- ✅ Валидация и санитизация данных
- ✅ Настройка CORS
- ✅ Логирование и мониторинг
- ✅ Практические примеры кода
📋 10 правил безопасности:
- Правило 1: Всегда используйте HTTPS
- Правило 2: Реализуйте аутентификацию
- Правило 3: Настройте авторизацию
- Правило 4: Добавьте rate limiting
- Правило 5: Валидируйте входные данные
- Правило 6: Защитите от SQL injection
- Правило 7: Защитите от XSS
- Правило 8: Правильно настройте CORS
- Правило 9: Используйте безопасные заголовки
- Правило 10: Логируйте и мониторьте
Правило 1: Всегда используйте HTTPS 🔒
1 Всегда используйте HTTPS
HTTPS (HyperText Transfer Protocol Secure) — это обязательный минимум безопасности. Без HTTPS все данные передаются в открытом виде и могут быть перехвачены злоумышленниками.
Почему это важно:
- Шифрование данных: Все данные шифруются между клиентом и сервером
- Защита от man-in-the-middle атак: Злоумышленник не может перехватить или изменить данные
- Защита паролей и токенов: Учетные данные передаются в зашифрованном виде
- Доверие пользователей: 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:
- Fixed window: Ограничение на фиксированный период времени
- Sliding window: Окно времени скользит вместе с запросами
- Token bucket: Клиенты получают токены с определенной скоростью
- Leaky bucket: Запросы накапливаются в очереди
// 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 Валидируйте входные данные
Валидация входных данных — это первая линия защиты от атак. Всегда проверяйте и санитизируйте данные от клиента перед обработкой.
Что валидировать:
- Тип данных: Число, строка, email, URL
- Длина: Минимальная и максимальная длина
- Формат: Email, телефон, дата
- Диапазон: Минимальное и максимальное значение
- Паттерны: Регулярные выражения
// Валидация с 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 атак:
- Reflected XSS: Вредоносный код отражается в ответе сервера
- Stored XSS: Вредоносный код сохраняется в базе данных
- DOM-based 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 Логируйте и мониторьте
Логирование и мониторинг помогают обнаружить атаки и проблемы безопасности в реальном времени. Без логов невозможно расследовать инциденты безопасности.
Что логировать:
- Авторизация: Успешные и неудачные попытки входа
- Запросы: Все API запросы с IP адресами
- Ошибки: Все ошибки и исключения
- Изменения данных: Критичные операции (удаление, изменение прав)
- Подозрительная активность: Необычные паттерны запросов
// Настройка логирования с 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 });
});
Мониторинг безопасности:
- Rate limiting превышен: Возможная DDoS атака
- Множественные 401/403: Попытки несанкционированного доступа
- Необычные паттерны запросов: Возможная атака
- Большой объем данных: Возможная утечка данных
💡 Инструменты для мониторинга:
- Prometheus + Grafana: Метрики и визуализация
- ELK Stack: Централизованное логирование
- Datadog / New Relic: APM и мониторинг
- Sentry: Отслеживание ошибок
Дополнительные рекомендации 🔒
✅ Чек-лист безопасности API:
- ✅ Используйте HTTPS для всех запросов
- ✅ Реализуйте аутентификацию (JWT, OAuth, API keys)
- ✅ Настройте авторизацию (RBAC, ACL)
- ✅ Добавьте rate limiting
- ✅ Валидируйте и санитизируйте все входные данные
- ✅ Защитите от SQL injection (prepared statements, ORM)
- ✅ Защитите от XSS (санитизация, CSP)
- ✅ Правильно настройте CORS
- ✅ Используйте security headers (Helmet)
- ✅ Логируйте и мониторьте активность
- ✅ Регулярно обновляйте зависимости
- ✅ Используйте секретные ключи из переменных окружения
- ✅ Проводите регулярный security audit
- ✅ Используйте API Gateway для дополнительной защиты
- ✅ Реализуйте backup и disaster recovery
Заключение
Безопасность API — это не одноразовая настройка, а непрерывный процесс. Эти 10 правил должны стать основой безопасности любого API. Однако помните:
💡 Помните:
- Безопасность — это многоуровневая защита, а не одно решение
- Регулярно обновляйте зависимости и проверяйте на уязвимости
- Проводите penetration testing и security audits
- Обучайте команду best practices безопасности
- Следите за новыми угрозами и уязвимостями
- Документируйте процессы безопасности
⚠️ Никогда не пренебрегайте:
Самые серьезные уязвимости часто возникают из-за простых ошибок: недостаточной валидации, слабых паролей, неправильной настройки CORS. Всегда следуйте принципу "недоверия к входным данным" и "принципу минимальных привилегий".
Создайте безопасный Mock API за 2 минуты
LightBox API поддерживает все стандарты безопасности: HTTPS, CORS, rate limiting. Создайте безопасный Mock API за 2 минуты и начните разработку без рисков.
Попробовать бесплатно →