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

SAST-сканеры: как автоматизировать поиск уязвимостей в коде и не сойти с ума

На одном проекте мы ловили баг безопасности вручную. Три сеньора, четыре часа code review, пропустили SQL-инъекцию через ORM. Она ушла в прод. Потом неделю чини

На одном проекте мы ловили баг безопасности вручную. Три сеньора, четыре часа code review, пропустили SQL-инъекцию через ORM. Она ушла в прод. Потом неделю чинили инцидент. После этого я понял: человеческий глаз не приспособлен искать уязвимости в потоке обычного кода. Нужна автоматизация.

SAST-сканеры — это инструменты статического анализа безопасности. Они читают код, не запуская его, и ищут паттерны уязвимостей. Звучит просто. На деле — куча нюансов.

Как вообще работает SAST

SAST-сканер парсит исходный код в абстрактное синтаксическое дерево (AST) или граф потока данных. Потом бегает по нему и ищет опасные паттерны: незафильтрованный ввод, небезопасные функции, утечки секретов.

Возьмём Python. Вот классическая SQL-инъекция через f-строку:

def get_user(user_id):
    query = f"SELECT * FROM users WHERE id = {user_id}"
    return db.execute(query)

Сканер видит: переменная user_id приходит извне, попадает в SQL-запрос без санитизации. Тревога. А вот этот код он пропустит спокойно:

def get_user(user_id):
    query = "SELECT * FROM users WHERE id = ?"
    return db.execute(query, (user_id,))

Разница в том, что во втором случае используется параметризованный запрос. Данные передаются отдельно от SQL-кода, и инъекция невозможна.

Проблема в том, что сканеры иногда орут на безопасный код. Ложные срабатывания — головная боль. На другом проекте мы внедрили SonarQube, и первая же проверка выдала 2000+ проблем. Из них реальных уязвимостей — штук пять. Остальное — легаси, false positives и низкоприоритетные замечания. Команда устала разруливать это за неделю.

Какие уязвимости ловят SAST-сканеры

Практически все классические уязвимости из OWASP Top 10. Давайте по конкретным примерам.

SQL-инъекции. Уже показал выше. Работают не только с чистым SQL, но и с ORM. В Django, например:

# Опасно!
User.objects.raw(f"SELECT * FROM auth_user WHERE username = '{username}'")

# Безопасно
User.objects.raw("SELECT * FROM auth_user WHERE username = %s", [username])

Хороший сканер знает специфику фреймворков и понимает, какие методы ORM безопасны, а какие — нет.

XSS — межсайтовый скриптинг. Вывод пользовательских данных без экранирования:

// Опасно в Express
res.send(`<h1>Hello, ${req.query.name}</h1>`);

// Безопасно
res.send(`<h1>Hello, ${escapeHtml(req.query.name)}</h1>`);

Утечки секретов. Хардкод паролей, API-ключей, токенов:

# Сканер найдёт это мгновенно
DATABASE_PASSWORD = "super_secret_123"
api_key = "sk-live-a1b2c3d4e5f6..."

Правильный подход — переменные окружения или секреты в CI/CD:

# .gitlab-ci.yml
variables:
  DATABASE_PASSWORD: $VAULT_DB_PASSWORD

Небезопасная десериализация. В Python это pickle, в Java — нативная сериализация, в PHP — unserialize. Пример:

import pickle

# Классическая дыра
data = request.get_data()
obj = pickle.loads(data)  # RCE гарантирован

Сканер пометит это как критическую уязвимость. И будет прав.

Path traversal. Когда пользователь может прочитать любой файл на сервере:

# Опасно
filename = request.args.get('file')
with open(f"/var/data/{filename}") as f:
    return f.read()

# Пользователь передаст: ../../../etc/passwd

Ровно то, что сканер должен обнаружить: пользовательский ввод попадает в файловые операции без валидации.

SAST против DAST: в чём разница

SAST анализирует исходный код. DAST (Dynamic Application Security Testing) сканирует работающее приложение, тыкая в него извне.

SAST находит уязвимости рано — ещё на этапе коммита. DAST — только когда приложение развёрнуто и запущено. Зато DAST видит проблемы, которые SAST не заметит: неправильная конфигурация сервера, проблемы с HTTPS, открытые порты.

Есть ещё IAST — интерактивный анализ. Агент прикручивается к приложению и мониторит запросы в рантайме, связывая их с конкретными строками кода. Точнее, чем DAST, но требует установки агента.

По-хорошему, нужно комбинировать. SAST в CI/CD пайплайне, DAST на стейджинге, IAST в тестовом окружении. Но это дорого и сложно. Большинство команд начинают с SAST — он проще в интеграции.

Какие SAST-сканеры есть на рынке

Бесплатные и open-source: Semgrep, Bandit (Python), ESLint security plugins, SpotBugs с плагинами (Java), Brakeman (Ruby on Rails).

Коммерческие: Checkmarx, Fortify, Veracode, Snyk Code. Мощные, с поддержкой, но дорогие.

Для российского рынка есть ограничения. Checkmarx и Fortify — иностранное ПО. Если санкции или требования по локализации — не подходят.

Мне нравятся Semgrep и Bandit. Semgrep — универсальный, поддерживает кучу языков, правила пишутся легко. Bandit — специализированный под Python, из коробки ловит основные проблемы.

Пример правила Semgrep для поиска SQL-инъекций:

rules:
  - id: python-sql-injection
    patterns:
      - pattern: $CURSOR.execute($QUERY, ...)
      - pattern-not: $CURSOR.execute("...", ...)
    message: "Possible SQL injection"
    severity: ERROR

Читается просто: ищем вызовы execute, где запрос — не строковый литерал. Скорее всего, туда подставляется переменная.

Интеграция в CI/CD пайплайн

Сам по себе сканер — вещь бесполезная. Он должен работать автоматически, на каждый пул-реквест или коммит.

Пример для GitLab CI с Semgrep:

# .gitlab-ci.yml
semgrep:
  image: returntocorp/semgrep
  script:
    - semgrep ci --config=auto
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
  allow_failure: false

Или Bandit для Python-проекта:

bandit:
  image: python:3.11
  script:
    - pip install bandit
    - bandit -r src/ -f json -o bandit-report.json
  artifacts:
    reports:
      sast: bandit-report.json

GitLab умеет показывать найденные уязвимости прямо в интерфейсе MR. Удобно.

Но есть нюанс. Если настроить allow_failure: false, пайплайн будет падать на каждой найденной проблеме. На легаси-проекте это парализует разработку. Поэтому я рекомендую такой подход:

Сначала запускаете сканер в режиме отчёта — allow_failure: true. Накапливаете базу проблем. Разруливаете критичные. Потом, когда проблем станет мало, включаете обязательную проверку.

False positives и как с ними жить

Главная беда SAST — ложные срабатывания. Сканер не понимает контекст. Он видит, что переменная пришла из request, и орёт. А то, что вы её провалидировали двумя строчками выше — не замечает.

Вот пример. Валидный код, который сканер может пометить как опасный:

user_id = request.args.get('id')
try:
    user_id = int(user_id)  # Валидация: только числа
except ValueError:
    abort(400)

# Сканер всё равно может ругаться на это
user = db.execute(f"SELECT * FROM users WHERE id = {user_id}")

Формально сканер прав: тут f-строка. Но после int() инъекция невозможна — останутся только цифры.

Решения несколько. Первое — переписать код правильно, чтобы сканер не ругался:

user_id = request.args.get('id')
user = db.execute("SELECT * FROM users WHERE id = ?", (user_id,))

Второе — подавить предупреждение комментарием:

user_id = int(request.args.get('id'))
# nosemgrep: python-sql-injection
user = db.execute(f"SELECT * FROM users WHERE id = {user_id}")

Третье — настроить правила сканера, исключить конкретные паттерны.

Я за первый вариант. Если сканер ругается — это сигнал, что код можно написать безопаснее. Не всегда, конечно. Но чаще всего.

Что в итоге

SAST-сканеры — не серебряная пуля. Они не найдут все уязвимости. Будут ложные срабатывания. Но они ловят те проблемы, которые человеческий глаз пропускает постоянно. SQL-инъекции, XSS, утечки секретов — типичные ошибки, которые повторяются из проекта в проект.

Внедряйте постепенно. Начните с одного языка, самого критичного кода. Настройте отчёты, разрулите легаси. Потом включайте обязательную проверку в пайплайне.

Если ищете инструмент, который умеет не только безопасность, но и общий code review — посмотрите Distiq. Это российский AI-бот для GitLab, GitHub и GitVerse. Он находит уязвимости, но также смотрит на архитектуру, производительность, читаемость кода. Интегрируется за пару минут через webhook. Мне нравится, что он не просто орёт "тут баг", а объясняет, что не так и как исправить.

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

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

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

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