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

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

Когда я работал в Яндексе, мы один раз обнаружили SQL injection в production коде через логи. Ребята потратили ночь на фиксинг. Если бы у них была SAST проверка

Когда я работал в Яндексе, мы один раз обнаружили SQL injection в production коде через логи. Ребята потратили ночь на фиксинг. Если бы у них была SAST проверка в CI/CD, это заметили бы за две секунды на этапе code review. Это больше не повторялось.

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

По-хорошему, SAST должен быть у каждой команды, которая пишет код. Не только у банков. Если у тебя есть хоть какие-то данные пользователей — ты уязвим.

Что такое SAST и почему это не просто "поиск by grep"

SAST работает на уровне абстрактного синтаксического дерева (AST). Инструмент парсит код, понимает структуру, отслеживает потоки данных. Не просто ищет паттерны по regex.

Например, простой grep найдёт все вхождения eval() в Python:

grep -r "eval(" .

Но SAST поймёт контекст. Видит, что eval() вызывается с пользовательским вводом? Опасно. Вызывается с константой, которую ты контролируешь? Может быть нормально.

Вот что делает правильный SAST:

Отслеживает потоки данных. Где данные входят в приложение (input sources), как они трансформируются, куда попадают (sinks). Если пользовательский ввод попал в SQL query без санитизации — поймает.

Понимает типы уязвимостей. SQL injection, XSS, CSRF, hardcoded credentials, небезопасная десериализация, race conditions. Каждый тип требует своего анализа.

Минимизирует false positives. Хороший SAST не будет плакать на каждый eval(). Он поймёт, опасна ли конкретная строка кода или нет.

На одном проекте мы внедрили SonarQube, и первый запуск выдал 2000+ замечаний. Половина — это шум. Пришлось тюнить rules, исключать тесты, настраивать confidence уровни. Вот почему выбор инструмента и его конфигурация — это не просто "включил и забыл".

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

Давай на конкретных примерах.

SQL Injection

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

# Плохо
user_id = request.args.get('id')
query = f"SELECT * FROM users WHERE id = {user_id}"
db.execute(query)

# Хорошо
user_id = request.args.get('id')
query = "SELECT * FROM users WHERE id = ?"
db.execute(query, (user_id,))

SAST видит, что user_id берётся из request.args (источник), попадает в SQL query через f-string (sink). Флагует как уязвимость.

Со второй версией всё чисто — параметризованный запрос, переменные передаются отдельно.

XSS (Cross-Site Scripting)

Пользовательский ввод попадает в HTML без экранирования:

// Плохо
const userName = urlParams.get('name');
document.getElementById('greeting').innerHTML = `Hello, ${userName}!`;

// Хорошо
const userName = urlParams.get('name');
document.getElementById('greeting').textContent = `Hello, ${userName}!`;

В первом случае если userName содержит <script>alert('hacked')</script>, это выполнится. SAST это видит.

Во втором случае используется textContent, который не интерпретирует HTML. Безопасно.

Hardcoded Credentials

Пароли, API ключи, токены прямо в коде:

# Плохо
DB_PASSWORD = "super_secret_123"
AWS_ACCESS_KEY = "AKIAIOSFODNN7EXAMPLE"

# Хорошо
DB_PASSWORD = os.environ.get('DB_PASSWORD')
AWS_ACCESS_KEY = os.environ.get('AWS_ACCESS_KEY')

SAST найдёт первый вариант. Не обязательно даже в переменной — может быть в конфиге, в строке подключения. Инструмент знает, как выглядят реальные API ключи и пароли.

Небезопасная Десериализация

В Python это критично с pickle:

# Плохо
import pickle
data = request.data
obj = pickle.loads(data)

# Хорошо
import json
data = request.data
obj = json.loads(data)

pickle.loads() может выполнить произвольный код, если данные подделаны. SAST это знает и флагует.

Race Conditions и TOCTOU

Время-of-check-time-of-use баги:

# Плохо
if os.path.exists(filename):
    with open(filename, 'r') as f:
        data = f.read()

# Хорошо
try:
    with open(filename, 'r') as f:
        data = f.read()
except FileNotFoundError:
    data = None

Между проверкой os.path.exists() и открытием файла может произойти что угодно. Файл удалят, переименуют, создадут symlink на что-то опасное. SAST видит эту логику.

Это не исчерпывающий список. SAST находит и dependency vulnerabilities (когда ты используешь небезопасную версию библиотеки), и проблемы с правами доступа, и логирование sensitive данных.

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

SAST смотрит на исходный код статически. Быстро, дёшево, находит проблемы на ранних стадиях разработки.

DAST запускает приложение и тестирует его как чёрный ящик. Отправляет вредоносные запросы, смотрит, как приложение реагирует. Находит уязвимости, которые видны только при запуске: неправильная конфигурация сервера, проблемы с аутентификацией, логические баги в бизнес-процессах.

SAST упустит уязвимость в конфигурации nginx. DAST это найдёт.

DAST не найдёт hardcoded пароль в исходном коде. SAST найдёт.

Правильный подход — использовать оба. SAST в CI/CD на каждый commit. DAST как отдельный этап тестирования перед продакшеном.

На практике большинство команд начинают с SAST, потому что он интегрируется проще и быстро даёт результаты. DAST требует более сложной инфраструктуры для запуска тестов.

Как внедрить SAST в pipeline: практические шаги

Ок, теория понятна. Как это внедрить?

Шаг 1: выбери инструмент. Есть несколько категорий:

Для стартапа я обычно рекомендую начать с Semgrep или SonarQube Community Edition. Обе открытые, бесплатные, легко интегрируются.

Шаг 2: интегрируй в CI/CD. Вот пример для GitHub Actions:

name: SAST Scan
on: [push, pull_request]

jobs:
  sast:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Run Semgrep
        uses: returntocorp/semgrep-action@v1
        with:
          config: >-
            p/owasp-top-ten
            p/cwe-top-25
            p/security-audit
      - name: Upload results
        if: always()
        uses: github/codeql-action/upload-sarif@v2
        with:
          sarif_file: semgrep.sarif

Для GitLab:

sast:
  stage: test
  image: returntocorp/semgrep
  script:
    - semgrep --config=p/owasp-top-ten --json -o sast-report.json .
  artifacts:
    reports:
      sast: sast-report.json

Шаг 3: настрой правила под твой проект. SAST из коробки часто выдаёт false positives. Нужно:

Для Semgrep можно создать .semgrep.yml:

rules:
  - id: custom-sql-injection
    pattern-either:
      - pattern: $DB.execute(f"...")
      - pattern: $DB.execute("..." + $VAR)
    message: Potential SQL injection
    languages: [python]
    severity: ERROR

Шаг 4: интегрируй результаты в процесс review. SAST должен быть не просто в logs. Результаты должны попадать в PR/MR как комментарии. Так разработчик видит сразу, что он сделал не так.

Например, с Distiq это происходит автоматически. Бот анализирует каждый MR, находит уязвимости и оставляет инлайн-комментарии с объяснением и рекомендацией, как исправить.

Частые ошибки при внедрении SAST

По моему опыту, команды часто наступают на грабли:

Включат все правила и утонут в алертах. Первый запуск SonarQube выдаёт сотни замечаний. Половина из них — это noise. Результат: разработчики игнорируют SAST.

Решение: начни с малого. Включи только критичные правила (OWASP Top 10, CWE Top 25), остальное добавляй постепенно.

Не настроят exclusions. SAST анализирует тесты, миграции БД, сгенерированный код. Там намеренно пишут небезопасный код. Нужно это исключить.

paths:
  exclude:
    - tests/
    - migrations/
    - node_modules/
    - venv/

Игнорируют результаты. Если SAST находит проблему, но её можно закомитить — инструмент теряет смысл. Нужна политика: либо фиксишь, либо explicitly игнорируешь с комментарием почему.

Не обновляют инструмент и rules. Новые уязвимости открываются постоянно. Если ты обновляешь SAST раз в год, ты пропустишь кучу проблем.

Честно? большинство команд, которые внедрили SAST правильно, потратили на это 2-3 недели на настройку и потом используют это как базу безопасности.

Примеры инструментов и их особенности

Semgrep — мой фаворит для быстрого старта. Написан на OCaml, быстрый, легко добавлять свои правила. Есть облачная версия (Semgrep Cloud) и self-hosted. Поддерживает кучу языков.

SonarQube — мощнее, больше функций, более сложная в настройке. Есть Community Edition (бесплатная) и коммерческие версии. Хороша для больших проектов.

Bandit (Python), ESLint (JavaScript) — специализированные. Если у тебя монорепо на одном языке, иногда лучше использовать специализированный инструмент.

GitHub Advanced Security / GitLab SAST — встроены в платформы. Удобно, не нужно отдельный сервис. Но меньше гибкости.

Snyk — облачный, фокусируется на dependency vulnerabilities (когда ты используешь небезопасную версию библиотеки). Хорош в комбинации с другими SAST.

Что выбрать? Если у тебя проект на GitHub — попробуй GitHub Advanced Security (он идёт в Pro плане). Если на GitLab — GitLab SAST. Если self-hosted и нужна максимальная гибкость — Semgrep или SonarQube.

Автоматизация в pipeline: реальный пример

Вот как я бы настроил SAST для типичного микросервиса на Python + Node.js:

# .gitlab-ci.yml
stages:
  - test
  - security

security:sast:
  stage: security
  image: returntocorp/semgrep
  script:
    - semgrep --config=p/owasp-top-ten 
             --config=p/cwe-top-25
             --config=p/python
             --config=p/javascript
             --json -o sast-report.json .
  artifacts:
    reports:
      sast: sast-report.json
  allow_failure: false  # Не пускаем в main если есть критичные баги

security:deps:
  stage: security
  image: python:3.11
  script:
    - pip install safety
    - safety check --json > deps-report.json || true
  artifacts:
    reports:
      dependency_scanning: deps-report.json

security:lint:
  stage: security
  script:
    - npm install

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

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

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

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