projects/org/ideal-shop/docs/SECURITY.md

SECURITY — Безопасность

Версия: 2.1.0
Дата: 2026-03-24


Уровни защиты

┌─────────────────────────────────────────┐
│            ПОЛЬЗОВАТЕЛЬ                 │
├─────────────────────────────────────────┤
│  1. HTTPS + HSTS                        │
├─────────────────────────────────────────┤
│  2. WAF / Rate Limiting                 │
├─────────────────────────────────────────┤
│  3. Аутентификация / Авторизация        │
├─────────────────────────────────────────┤
│  4. Валидация данных                    │
├─────────────────────────────────────────┤
│  5. База данных (шифрование)            │
└─────────────────────────────────────────┘

1. HTTPS/SSL

Требования

Параметр Требование
Протокол TLS 1.2+ (TLS 1.3 предпочтительно)
Сертификат Let's Encrypt или платный
Срок действия Автообновление за 30 дней
Редирект HTTP → HTTPS (301)

Nginx конфигурация

server {
    listen 80;
    server_name example.com;
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    server_name example.com;

    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

    # Современные настройки
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
    ssl_prefer_server_ciphers off;

    # OCSP Stapling
    ssl_stapling on;
    ssl_stapling_verify on;
}

HSTS

add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;

2. HTTP Security Headers

Обязательные

# Запрет встраивания в iframe
add_header X-Frame-Options "SAMEORIGIN" always;

# Защита от XSS
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;

# Referrer Policy
add_header Referrer-Policy "strict-origin-when-cross-origin" always;

# Permissions Policy
add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always;

Content Security Policy (CSP)

add_header Content-Security-Policy "
    default-src 'self';
    script-src 'self' 'unsafe-inline' https://mc.yandex.ru https://www.google-analytics.com;
    style-src 'self' 'unsafe-inline' https://fonts.googleapis.com;
    img-src 'self' data: https: blob:;
    font-src 'self' https://fonts.gstatic.com;
    connect-src 'self' https://mc.yandex.ru https://www.google-analytics.com;
    frame-ancestors 'self';
    form-action 'self';
    base-uri 'self';
" always;

Проверка

# Проверить заголовки
curl -I https://example.com

# Онлайн-проверка
# https://securityheaders.com
# https://observatory.mozilla.org

3. Защита от атак

XSS (Cross-Site Scripting)

// Экранирование вывода
echo htmlspecialchars($userInput, ENT_QUOTES, 'UTF-8');

// Twig (автоматически)
{{ user.name }}  {# экранируется #}
{{ user.bio|raw }}  {# НЕ экранируется - осторожно! #}

SQL Injection

// Плохо
$query = "SELECT * FROM users WHERE id = " . $_GET['id'];

// Хорошо - prepared statements
$stmt = $pdo->prepare("SELECT * FROM users WHERE id = ?");
$stmt->execute([$_GET['id']]);

// Drupal - всегда использовать Database API
$query = \Drupal::database()->select('users', 'u')
    ->fields('u', ['name'])
    ->condition('uid', $uid)
    ->execute();

CSRF (Cross-Site Request Forgery)

// Форма с токеном
<form method="POST">
    <input type="hidden" name="csrf_token" value="<?= $csrfToken ?>">
    ...
</form>

// Проверка токена
if (!hash_equals($_SESSION['csrf_token'], $_POST['csrf_token'])) {
    throw new Exception('CSRF token mismatch');
}

// Drupal - автоматически в Form API

Clickjacking

# Уже в headers выше
add_header X-Frame-Options "SAMEORIGIN" always;

4. Rate Limiting

Nginx

# Определение зоны
limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;
limit_req_zone $binary_remote_addr zone=login:10m rate=1r/s;

# Применение
location /api/ {
    limit_req zone=api burst=20 nodelay;
}

location /user/login {
    limit_req zone=login burst=5;
}

Fail2ban

# /etc/fail2ban/jail.local
[nginx-limit-req]
enabled = true
filter = nginx-limit-req
action = iptables-multiport[name=nginx, port="http,https"]
logpath = /var/log/nginx/error.log
findtime = 600
maxretry = 10
bantime = 3600

Cloudflare

Правило Действие
Rate limit API 100 req/min → Challenge
Rate limit login 5 req/min → Block
Bot protection Challenge suspicious

5. Аутентификация

Пароли

Требование Значение
Минимальная длина 8 символов
Сложность Буквы + цифры
Хэширование bcrypt (cost 12+) или Argon2
Хранение Только хэш, никогда plaintext
// Хэширование
$hash = password_hash($password, PASSWORD_ARGON2ID);

// Проверка
if (password_verify($password, $hash)) {
    // OK
}

Сессии

// Настройки PHP
ini_set('session.cookie_httponly', 1);
ini_set('session.cookie_secure', 1);
ini_set('session.cookie_samesite', 'Lax');
ini_set('session.use_strict_mode', 1);

// Регенерация ID после логина
session_regenerate_id(true);

2FA (двухфакторная)

Метод Надёжность
SMS Низкая (SIM swap)
Email Средняя
TOTP (Google Auth) Высокая
WebAuthn/Passkey Очень высокая

6. Защита данных

Шифрование в базе

// Шифрование чувствительных данных
$encrypted = openssl_encrypt(
    $cardNumber,
    'aes-256-gcm',
    $key,
    OPENSSL_RAW_DATA,
    $iv,
    $tag
);

// Хранить: $iv . $tag . $encrypted (base64)

PCI DSS (для карт)

Требование Решение
Не хранить CVV Никогда
Не хранить полный PAN Только токен от эквайера
Шифрование AES-256
Логирование Маскировать карты

Рекомендация: Использовать hosted payment page (ЮKassa, Тинькофф) — карты не проходят через ваш сервер.

Персональные данные (152-ФЗ)

Данные Защита
Телефон Маскировать в логах (+7***45-67)
Email Маскировать (u***@mail.ru)
Адрес Шифровать или минимизировать
Пароль Только хэш

7. Резервное копирование

Стратегия 3-2-1

Расписание

Тип Частота Хранение
Полный бэкап Еженедельно 4 недели
Инкрементальный Ежедневно 7 дней
БД (mysqldump) Каждые 6 часов 48 часов
Файлы (rsync) Ежедневно 7 дней

Скрипт бэкапа

#!/bin/bash
DATE=$(date +%Y%m%d_%H%M)
BACKUP_DIR="/backups"

# База данных
mysqldump -u root shop_db | gzip > $BACKUP_DIR/db_$DATE.sql.gz

# Файлы
tar -czf $BACKUP_DIR/files_$DATE.tar.gz /var/www/shop/

# Отправка в S3
aws s3 cp $BACKUP_DIR/db_$DATE.sql.gz s3://backups/
aws s3 cp $BACKUP_DIR/files_$DATE.tar.gz s3://backups/

# Удаление старых (7 дней)
find $BACKUP_DIR -mtime +7 -delete

Проверка восстановления


8. Логирование и мониторинг

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

Событие Уровень
Успешный вход INFO
Неудачный вход WARNING
Смена пароля INFO
Изменение email INFO
Заказ создан INFO
Ошибка оплаты ERROR
Подозрительная активность ALERT

Формат логов

{
  "timestamp": "2025-12-21T12:00:00Z",
  "level": "WARNING",
  "event": "login_failed",
  "user_id": null,
  "ip": "192.168.1.1",
  "user_agent": "Mozilla/5.0...",
  "details": {
    "email": "u***@mail.ru",
    "reason": "invalid_password",
    "attempt": 3
  }
}

Алерты

Событие Действие
5 неудачных входов Email администратору
10 неудачных входов Блокировка IP на 1 час
Массовые 4xx/5xx SMS/Telegram
Падение сервера Звонок

9. DDoS защита

Базовая (Nginx)

# Ограничение соединений
limit_conn_zone $binary_remote_addr zone=conn:10m;
limit_conn conn 20;

# Ограничение запросов
limit_req_zone $binary_remote_addr zone=req:10m rate=5r/s;
limit_req zone=req burst=10 nodelay;

# Таймауты
client_body_timeout 10s;
client_header_timeout 10s;
keepalive_timeout 15s;
send_timeout 10s;

Cloudflare

Режим Когда включать
Under Attack Активная атака
I'm Under Attack Mode Серьёзная атака
Rate Limiting Постоянно
Bot Fight Mode Постоянно

Защита форм

<!-- Honeypot -->
<input type="text" name="website" style="display:none" tabindex="-1">

<!-- Проверка на сервере -->
if (!empty($_POST['website'])) {
    // Бот
    exit;
}

10. Обновления

Регулярные обновления

Компонент Частота
ОС (security) Еженедельно
CMS (Drupal) При выходе security release
PHP При выходе security release
Nginx При выходе security release
Зависимости (npm, composer) Ежемесячно

Автоматизация

# Ubuntu - автообновления безопасности
apt install unattended-upgrades
dpkg-reconfigure unattended-upgrades

# Composer audit
composer audit

# npm audit
npm audit
npm audit fix

Drupal Security

# Проверка обновлений
drush pm:security

# Обновление
composer update drupal/core --with-dependencies
drush updatedb
drush cache:rebuild

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

Критично

Важно

Рекомендуется


12. Контакты при инциденте

Подозрение на взлом:
1. Изолировать сервер (отключить от сети)
2. Сохранить логи и состояние
3. Уведомить: security@company.com
4. Не удалять улики
5. Документировать всё

Утечка данных (152-ФЗ):
1. Уведомить Роскомнадзор (72 часа)
2. Уведомить пострадавших
3. Принять меры по устранению

Версия: 2.1.0