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

SonarQube SAST: Как на самом деле строить защиту кода

На одном проекте мы нашли SQL-инъекцию через год после того, как она попала в продакшн. Классика — никто не делал code review с точки зрения безопасности, все с

На одном проекте мы нашли SQL-инъекцию через год после того, как она попала в продакшн. Классика — никто не делал code review с точки зрения безопасности, все смотрели на архитектуру и нейминг переменных. Уязвимость жила в малоиспользуемом фильтре отчётов. Когда узнали — было неловко.

После этого я начал разбираться с SAST. Оказалось, что большинство команд относятся к этому как к «ещё одной проверке в пайплайне», которая иногда кричит ложными срабатываниями. Но если разобраться, SonarQube SAST — это не про галочку в чек-листе, а про системный подход к безопасности.

Что такое SAST и чем отличается от DAST

SAST — Static Application Security Testing. Анализируем исходный код, не запуская его. Сканируем исходники, находим паттерны, которые могут привести к уязвимостям.

DAST — Dynamic Application Security Testing. Работаем с работающим приложением. Отправляем запросы, смотрим на ответы, пытаемся сломать.

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

SonarQube делает именно SAST. Парсит код, строит абстрактное синтаксическое дерево, ищет паттерны уязвимостей. Если коротко — знает, как выглядит плохой код, и сообщает вам об этом.

Как SonarQube SAST работает под капотом

SonarQube не просто ищет регулярками. Это было бы слишком просто и давало бы кучу ложных срабатываний.

Сначала парсер строит AST — абстрактное синтаксическое дерево. Потом анализатор проходит по дереву и отслеживает поток данных. Отслеживает, откуда приходят данные, куда уходят, не попадают ли они в опасные функции без санитизации.

Пример. Вот код с SQL-инъекцией:

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

SonarQube видит: переменная user_id приходит извне, попадает в строку запроса без обработки, эта строка уходит в db.execute. Трассировка показывает путь от источника до приёмника. Это и есть уязвимость.

Правильный вариант:

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

Здесь параметризованный запрос. Данные и код разделены. SonarQube это понимает и не ругается.

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

SQL-инъекции — самая очевидная категория. Но есть и другие.

XSS — Cross-Site Scripting. Вставляем пользовательский ввод в HTML без экранирования:

// Плохо
element.innerHTML = userInput;

// Хорошо
element.textContent = userInput;

Path traversal. Когда пользователь может указать путь к файлу:

# Плохо
filename = request.args.get('file')
with open(f"/var/data/{filename}") as f:
    return f.read()

# Хорошо — валидация
import os
filename = request.args.get('file')
safe_path = os.path.join("/var/data", os.path.basename(filename))

Hardcoded credentials. Пароли и ключи прямо в коде:

# SonarQube это найдёт
DATABASE_PASSWORD = "super_secret_123"
API_KEY = "sk-live-abc123def456"

Использование небезопасных функций. В Python это eval(), exec(), pickle.loads() с непроверенными данными. В Java — десериализация непроверенных объектов.

Weak cryptography. MD5 для паролей, слабые ключи, неправильные режимы шифрования:

# Плохо
import hashlib
hashed = hashlib.md5(password.encode()).hexdigest()

# Хорошо
import bcrypt
hashed = bcrypt.hashpw(password.encode(), bcrypt.gensalt())

Настраиваем SonarQube для реального проекта

Честно? Из коробки SonarQube работает, но его нужно адаптировать под ваш стек.

Сначала определите quality profile. Для Java, Python, JavaScript есть готовые профили с правилами безопасности. Можно включить все — получите максимум покрытия, но и ложных срабатываний будет больше.

Я обычно делаю так: включаю все правила с severity Critical и Blocker. Правила с Major разбираю вручную и выключаю те, которые не подходят под архитектуру проекта.

Конфигурация через sonar-project.properties:

sonar.projectKey=my-project
sonar.sources=src
sonar.tests=tests
sonar.python.version=3.9
sonar.exclusions=**/migrations/**,**/tests/**
sonar.issue.ignore.multicriteria=e1
sonar.issue.ignore.multicriteria.e1.ruleKey=python:S100
sonar.issue.ignore.multicriteria.e1.resourceKey=**/models.py

Здесь мы исключаем миграции и тесты из анализа и игнорируем правило нейминга в моделях. Потому что иногда оно не имеет значения.

Интеграция в CI/CD pipeline

SonarQube без автоматизации — бесполезен. Точнее, полезен, но ровно до тех пор, пока кто-нибудь не забудет запустить сканирование перед релизом.

В GitLab CI это выглядит так:

stages:
  - test
  - security

sonarqube-check:
  stage: security
  image: sonarsource/sonar-scanner-cli
  script:
    - sonar-scanner 
      -Dsonar.projectKey=${CI_PROJECT_NAME}
      -Dsonar.sources=src
      -Dsonar.host.url=${SONAR_HOST}
      -Dsonar.login=${SONAR_TOKEN}
  only:
    - merge_requests
    - main

В GitHub Actions похоже:

name: SonarQube Scan
on:
  push:
    branches: [main]
  pull_request:
jobs:
  sonarqube:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - name: SonarQube Scan
        uses: sonarsource/sonarqube-scan-action@master
        env:
          SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
          SONAR_HOST_URL: ${{ secrets.SONAR_HOST }}

Ключевой момент — fetch-depth: 0. SonarQube нужна история коммитов для корректного анализа новых изменений.

Quality Gates: блокируем или предупреждаем

Quality Gate — это набор условий, при которых сканирование считается пройденным или проваленным. Можно настроить жёстко: есть Critical уязвимость — билд падает.

Пример настроек Quality Gate:

Я рекомендую начинать мягко. Настроить уведомления, но не блокировать билды. Через месяц, когда команда привыкнет и разберёт накопившиеся проблемы — включать блокировку.

Иначе будет так: разработчики начнут ненавидеть SonarQube, найдут способы обойти проверки, и весь смысл потеряется.

Ложные срабатывания и как с ними жить

По моему опыту, где-то 15-20% найденных уязвимостей — ложные срабатывания. Код безопасен, но анализатор не понимает контекст.

Пример. SonarQube ругается на SQL-инъекцию:

def get_user(table_name):
    # SonarQube видит переменную в SQL и кричит
    query = f"SELECT * FROM {table_name} WHERE id = ?"
    return db.execute(query, (user_id,))

Но если table_name приходит из конфига, а не от пользователя — это безопасно. Нужно объяснить SonarQube, что переменная доверенная:

ALLOWED_TABLES = ['users', 'orders', 'products']

def get_user(table_name):
    if table_name not in ALLOWED_TABLES:
        raise ValueError("Invalid table")
    query = f"SELECT * FROM {table_name} WHERE id = ?"
    return db.execute(query, (user_id,))

Теперь есть валидация. SonarQube это понимает и перестаёт ругаться.

Если всё равно ложное срабатывание — можно пометить как «Won't Fix» прямо в интерфейсе. Главное — не массово закрывать, не разбираясь. Иначе пропустите реальную уязвимость.

Ограничения SonarQube SAST

SonarQube не понимает бизнес-логику. Он найдёт SQL-инъекцию, но не поймёт, что пользователь может получить доступ к чужим данным через ID в URL.

SAST не видит уязвимости в сторонних библиотеках. Для этого нужен SCA — Software Composition Analysis. SonarQube умеет, но это отдельная история.

Сложные цепочки данных анализируются не всегда корректно. Если данные проходят через 5 функций и 3 файла — SonarQube может потерять след.

Ну и главное — SAST не заменяет DAST и пентесты. Он дополняет их. Защита в глубину, многослойный подход, все дела.

Альтернативы и когда они нужны

SonarQube — не единственный игрок. Есть Semgrep, CodeQL, Checkmarx, Snyk Code.

Semgrep — легковесный, быстро настраивается, хорош для кастомных правил. CodeQL — мощный, но сложный, от GitHub. Snyk Code — облачный, с хорошей интеграцией в IDE.

Выбор зависит от команды и процессов. SonarQube выигрывает у других, когда нужна самостоятельная установка, гибкая настройка правил и интеграция с существующей инфраструктурой.

Для российских компаний сейчас ещё и важен фактор локализации. SonarQube можно развернуть на своих серверах, данные не уходят за рубеж.


Если хотите пойти дальше автоматизации безопасности — посмотрите Distiq. Это AI-бот для code review, который встраивается в GitLab, GitHub и GitVerse. Он анализирует каждый MR, находит баги, уязвимости и проблемы с производительностью, оставляет инлайн-комментарии. Работает как второй глазами на ревью — только быстрее и не устаёт. Я бы использовал его вместе с SonarQube: статический анализ ловит паттерны, AI находит логические проблемы.

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

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

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

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