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

SAST сканирование: как ловить уязвимости в коде до боевого сервера

Я работал в компании, где один junior закоммитил SQL-инъекцию прямо в production. Утром пришёл alert, днём уже была инцидент-постмортем. Мог бы не быть, если бы

Я работал в компании, где один junior закоммитил SQL-инъекцию прямо в production. Утром пришёл alert, днём уже была инцидент-постмортем. Мог бы не быть, если бы у них работало нормальное SAST сканирование. Но давайте по порядку.

SAST (Static Application Security Testing) — это анализ кода без его выполнения. Инструмент смотрит на исходник, находит потенциальные уязвимости, и кричит об этом. Звучит просто, но в деталях куча нюансов.

Что вообще ловит SAST и почему это важно

SAST работает на уровне исходного кода. Инструмент парсит дерево синтаксиса, ищет опасные паттерны, отслеживает потоки данных. Это не блэк-бокс тестирование — это белый ящик, где видно всё.

Что он может найти:

Уязвимости ввода-вывода. SQL-инъекции, XSS, command injection — когда пользовательские данные напрямую попадают в опасные функции без валидации. По моему опыту, это топ-3 уязвимостей в production коде. Вот пример:

# Так не надо
user_id = request.args.get('id')
query = f"SELECT * FROM users WHERE id = {user_id}"
db.execute(query)

# SAST сразу заметит, что user_id не экранирован

Хардкод секретов. API ключи, пароли, токены прямо в коде. Я видел, как разработчик добавил AWS ключ в переменную окружения, а потом закоммитил .env файл. SAST ловит такое с вероятностью 80-90%.

Небезопасное использование криптографии. Слабые алгоритмы хеширования, жёсткие IV для шифрования, использование MD5 для паролей — классика.

Race conditions и проблемы с конкурентностью. Сложнее ловить, но хороший SAST справляется.

Проблемы десериализации. Когда десериализуешь JSON без проверки типов, может быть беда.

Статистика: по данным OWASP, примерно 70% уязвимостей можно было бы найти статическим анализом ещё на этапе разработки. Но большинство компаний начинают проверку только перед релизом или, о ужас, после инцидента.

SAST vs DAST: в чём разница и почему оба нужны

Часто путают SAST и DAST. Дай я расскажу про каждый.

SAST — статический анализ. Смотрит на код, не запуская его. Быстро, но иногда много ложных срабатываний. Ловит проблемы рано, в IDE разработчика.

DAST — динамический анализ. Запускает приложение, отправляет в него фаззинг, ловит ошибки в runtime. Более реалистичен, но медленнее. И сложнее интегрировать в CI/CD.

По-хорошему, нужны оба. SAST — для быстрого feedback на этапе разработки. DAST — для глубокого тестирования перед production.

# Типичный pipeline с обоими проверками
stages:
  - build
  - sast
  - test
  - dast
  - deploy

sast_scan:
  stage: sast
  script:
    - semgrep --config=p/security-audit src/
  artifacts:
    reports:
      sast: sast-report.json

dast_scan:
  stage: dast
  script:
    - owasp-zap-scan http://localhost:8080
  artifacts:
    reports:
      dast: dast-report.json

На одном проекте в Яндексе мы внедрили обе проверки. SAST ловил 90% проблем на этапе MR, DAST находил оставшиеся 10% и некоторые логические уязвимости, которые SAST не видит.

Инструменты SAST: что выбрать и как не облажаться

На рынке куча инструментов. Дам краткий обзор.

Semgrep — мой фаворит для стартапов и небольших команд. Open source, быстро, гибкие правила. Пишешь свои проверки на простом DSL. Бесплатный, и enterprise версия тоже разумная. Я использовал его на трёх проектах — нареканий не было.

# Установка и использование
pip install semgrep
semgrep --config=p/security-audit --json -o report.json src/

SonarQube — тяжёлая артиллерия. Хорош для больших компаний с сложной структурой кода. Красивый Dashboard, интеграция со всем. Но дорогой и требует сервера.

Checkmarx — коммерческий, мощный. Хорош если у вас серьёзные требования по безопасности (финтех, госконтракты). Дорого.

Bandit (для Python), ESLint с плагинами (для JS), FindBugs (для Java) — специализированные инструменты. Проще настроить под язык, но узкие.

Snyk — находит уязвимости в зависимостях. Это не совсем SAST в классическом смысле, но очень полезно.

Если честно? Большинство команд начинают с Semgrep. Это оптимальное соотношение мощности и простоты. Потом, когда растут, переходят на SonarQube или Checkmarx.

Типичные уязвимости, которые ловит SAST

Давай разберу конкретные примеры уязвимостей и как их находить.

SQL-инъекция

Классика жанра. Пользователь передаёт параметр, он попадает прямо в query.

# Уязвимо
username = request.form.get('username')
password = request.form.get('password')
query = f"SELECT * FROM users WHERE username='{username}' AND password='{password}'"
result = db.execute(query)

# SAST видит, что username и password используются в f-string без экранирования
# Пользователь вводит: admin' --
# Запрос становится: SELECT * FROM users WHERE username='admin' --' AND password='...'
# Пароль не проверяется, вход как admin

Как это ловить:

# Правильно
query = "SELECT * FROM users WHERE username=? AND password=?"
result = db.execute(query, (username, password))

SAST ищет паттерны типа f"SELECT ... {variable}" или "... " + variable + " ..." и флагирует их.

XSS (Cross-Site Scripting)

Когда вывод пользовательского контента не экранирован.

// Уязвимо
const comment = req.query.comment;
res.send(`<div>${comment}</div>`);

// Пользователь отправляет: <img src=x onerror="alert('hacked')">
// В HTML попадает: <div><img src=x onerror="alert('hacked')"></div>
// Скрипт выполняется в браузере у каждого, кто откроет страницу
// Правильно
const comment = req.query.comment;
res.send(`<div>${escapeHtml(comment)}</div>`);

// Или в шаблонизаторе (вроде Handlebars, EJS)
// они экранируют по умолчанию: {{comment}}

SAST ищет innerHTML, eval, и прямые вставки в HTML без экранирования.

Хардкод секретов

# Уязвимо
AWS_KEY = "AKIAIOSFODNN7EXAMPLE"
DB_PASSWORD = "mypassword123"
API_TOKEN = "ghp_1234567890abcdefghijklmnopqrstuvwxyz"

SAST сканирует на regex паттерны AWS ключей, GitHub токенов, и другие известные форматы. Современные инструменты (вроде git-secrets, truffleHog) это ловят с точностью 95%.

Unsafe deserialization

# Уязвимо
import pickle
data = request.data
user = pickle.loads(data)  # Если data не от нас, это RCE

# Правильно
import json
data = request.data
user = json.loads(data)  # JSON безопаснее

Использование известных уязвимых библиотек

Flask 1.0.0 имеет CVE-2019-1010083
Django 2.0.0 имеет CVE-2019-6975
requests 2.20.0 имеет уязвимость в SSL verification

SAST проверяет версии зависимостей против базы CVE.

Как интегрировать SAST в pipeline

Самое важное — это не просто запустить сканер, а встроить его в workflow так, чтобы не было боли.

Вариант 1: Semgrep в GitLab CI

stages:
  - scan
  - test

semgrep_scan:
  stage: scan
  image: returntocorp/semgrep
  script:
    - semgrep --config=p/security-audit 
              --json 
              -o sast-report.json 
              --gitlab-json 
              -o gitlab-sast.json 
              src/
  artifacts:
    reports:
      sast: gitlab-sast.json
    expire_in: 30 days
  allow_failure: false

Вариант 2: SonarQube в pipeline

sonarqube_scan:
  stage: scan
  image: sonarsource/sonar-scanner-cli:latest
  script:
    - sonar-scanner
      -Dsonar.projectKey=my-app
      -Dsonar.sources=src/
      -Dsonar.host.url=$SONAR_HOST_URL
      -Dsonar.login=$SONAR_TOKEN

Вариант 3: Snyk для зависимостей

npm install -g snyk
snyk auth $SNYK_TOKEN
snyk test --severity-threshold=high

Ключевые моменты при интеграции:

  1. Не блокируй сразу. Первые запуски SAST будут много ложных срабатываний. Используй allow_failure: true первые 2-3 недели, пока не отфильтруешь noise.

  2. Baseline. Сначала просканируй весь codebase, создай baseline. Потом проверяй только новые коммиты.

  3. Настройка правил. Не все уязвимости одинаково важны. Высокий приоритет SQL-инъекциям, XSS, command injection. Low priority — стиль кода, логирование.

  4. Feedback разработчикам. Самое важное — быстрый feedback. Если SAST запускается 10 минут, разработчик забудет, над чем работал. Оптимально — до 2 минут.

  5. Игнорирование ложных срабатываний. Иногда нужно явно заигнорировать проверку в коде:

# nosemgrep: python.lang.security.audit.sql-injection
query = f"SELECT * FROM logs WHERE id = {user_id}"
# Это безопасно, потому что user_id валидируется выше

Реальная история: как SAST спас нас от инцидента

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

Одна из них: junior разработчик писал логирование ошибок. Логировал весь объект request, включая headers. А в headers могли быть Authorization токены. Если бы это попало в production, логи содержали бы чувствительные данные.

SAST заметил паттерн "логирование полного request объекта" и заметил. Мы исправили за 5 минут.

Без SAST это бы заметили только через полгода, когда кто-то посмотрел бы логи и сказал "стоп, тут токены!".

Когда SAST неэффективен

Честно скажу, SAST — не панацея.

Логические уязвимости. SAST не поймёт, что в твоём коде есть race condition, которая позволяет скупить все товары по цене 1 копейка. Это нужно тестировать динамически.

Конфигурационные ошибки. Если ты настроил AWS S3 bucket доступным для всех через web UI, SAST это не увидит.

Уязвимости на уровне архитектуры. Если у тебя нет rate limiting, SAST не поможет.

Контекстные уязвимости. Иногда код выглядит уязвимым, но благодаря другому коду выше, это безопасно. SAST часто тут ошибается.

Поэтому SAST — это первая линия защиты. Потом идут code review человека, тестирование, DAST, и security аудит перед production.

Как выбрать инструмент для твоего проекта

Стартап, 5-10 разработчиков, Python/JS: Semgrep + git-secrets. Бесплатно, быстро, достаточно.

Средняя компания, 20+ разработчиков, несколько языков: SonarQube Community (бесплатно) или Semgrep Pro. Нужен более красивый dashboard и история.

Большая компания, высокие требования по безопасности: Checkmarx или Fortify. Дорого, но мощно.

Критичны зависимости: Snyk обязательно добавь на верх всего остального.


Если ты настраиваешь SAST с ну

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

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

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

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