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

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

Если ты думаешь, что security это забота тестировщиков — забудь. Половина уязвимостей проскакивает мимо всех потому что их ищут в конце процесса разработки. А и

Если ты думаешь, что security это забота тестировщиков — забудь. Половина уязвимостей проскакивает мимо всех потому что их ищут в конце процесса разработки. А искать нужно сразу, во время написания кода.

SAST (Static Application Security Testing) — это анализ исходного кода без его запуска. Просто смотрим на код и говорим: "Тут SQL injection", "Вот XSS", "А здесь — неправильная валидация". Звучит просто? На деле это самый эффективный способ поймать баги безопасности на ранней стадии.

За 10 лет в разработке я видел, как один упущенный SQL injection стоил компании недели переделки и потери доверия пользователей. Так что это не просто best practice — это необходимость.

Что такое SAST и почему это работает

SAST анализирует код статически — без выполнения. Инструмент смотрит на исходники, строит граф потока данных (data flow graph) и проверяет: есть ли опасные комбинации?

Например:

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

SAST инструмент видит: пользовательский ввод request.args.get() напрямую попадает в SQL запрос. Красная лампочка.

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

Тут ввод идёт через параметризованный запрос. Безопасно.

Главное преимущество SAST — скорость. Проверяем код за секунды, ещё до merge request. Не нужно дождаться deployment и пентеста.

Минус один: SAST не видит логику, которая работает только в runtime. Если уязвимость проявляется только в определённых условиях при работающей системе — SAST может её пропустить. Для этого есть DAST (Dynamic Application Security Testing), но это отдельная история.

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

На одном проекте мы внедрили SAST и за первую неделю нашли 47 потенциальных проблем. Две из них были критичные. Рассказываю, что чаще всего попадает:

SQL Injection

Классика жанра. Пользовательский ввод в SQL запрос без экранирования.

# Плохо
username = request.form['username']
password = request.form['password']
query = f"SELECT * FROM users WHERE username='{username}' AND password='{password}'"
result = db.execute(query)

Юзер вводит ' OR '1'='1 и получает доступ без пароля.

SAST видит это сразу. Решение простое — параметризованные запросы:

# Хорошо
username = request.form['username']
password = request.form['password']
query = "SELECT * FROM users WHERE username=? AND password=?"
result = db.execute(query, (username, password))

Cross-Site Scripting (XSS)

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

// Плохо
const comment = req.query.text;
res.send(`<div>${comment}</div>`);

Юзер пишет: <script>alert('hacked')</script> и скрипт выполняется в браузере каждого, кто смотрит этот комментарий.

// Хорошо
const comment = req.query.text;
const escaped = escapeHtml(comment);
res.send(`<div>${escaped}</div>`);

Или просто используй шаблонизаторы, которые экранируют по умолчанию (например, Jinja в Flask).

Hardcoded Credentials

Пароли и токены прямо в коде. Встречается чаще, чем ты думаешь.

# Плохо
API_KEY = "sk-1234567890abcdef"
DB_PASSWORD = "admin123"

SAST инструмент видит строки, похожие на credentials, и бьёт тревогу. Решение: используй переменные окружения или secrets manager.

# Хорошо
import os
API_KEY = os.getenv('API_KEY')
DB_PASSWORD = os.getenv('DB_PASSWORD')

Path Traversal

Когда пользователь может указать любой путь к файлу на сервере.

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

Юзер пишет ../../etc/passwd и получает доступ к файлам сервера.

# Хорошо
import os
from pathlib import Path

filename = request.args.get('file')
base_dir = Path("/uploads/")
file_path = (base_dir / filename).resolve()

# Проверяем, что файл внутри /uploads/
if not str(file_path).startswith(str(base_dir)):
    return "Access denied", 403

with open(file_path) as f:
    return f.read()

Неправильная валидация входных данных

Когда ты проверяешь только на клиенте или проверка неполная.

// Плохо
if (email.includes('@')) {
    // отправляем письмо
}

Это не валидация. Нужна по-настоящему:

# Хорошо
from email_validator import validate_email, EmailNotValidError

try:
    valid = validate_email(email)
    email = valid.email
except EmailNotValidError:
    return "Invalid email", 400

SAST инструменты: что выбрать

На рынке куча решений. Вот что я знаю по опыту:

SonarQube — король enterprise. Поддерживает 27 языков, отличная интеграция с CI/CD, удобная веб-морда. Минус: дорого и сложно настраивать.

Semgrep — мне нравится больше. Пишешь правила на простом YAML, проверяет быстро. Open source, легко интегрируется в любой pipeline.

# Пример правила Semgrep против SQL injection
rules:
  - id: sql-injection
    pattern-sources:
      - patterns:
          - pattern: request.args.get(...)
          - pattern: request.form[...]
    pattern-sinks:
      - patterns:
          - pattern: $DB.execute(f"...")
    message: "SQL injection vulnerability"
    severity: ERROR

Snyk — хорош для зависимостей. Ловит уязвимости в npm пакетах, pip библиотеках и т.д. Плюс ещё и SAST делает.

OWASP Dependency-Check — бесплатный, простой, хорошо работает для поиска уязвимостей в зависимостях.

Pylint, ESLint, Checkstyle — встроенные инструменты для каждого языка. Не full-featured SAST, но для базовых проверок достаточно.

Честно? Для большинства проектов хватает Semgrep + встроенные линтеры. Дополни OWASP Dependency-Check и спи спокойно.

Как встроить SAST в pipeline

Теория — теория, но главное это action. Вот как это работает в реальности:

# .gitlab-ci.yml пример
stages:
  - scan
  - test
  - build

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

Или через GitHub Actions:

# .github/workflows/sast.yml
name: SAST Scan

on: [push, pull_request]

jobs:
  semgrep:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: returntocorp/semgrep-action@v1
        with:
          config: p/owasp-top-ten

После этого каждый push автоматически проверяется. Если нашли проблему — блокируем merge request. Разработчик видит конкретные замечания прямо в коде.

Важный момент: не делай SAST слишком strict на старых проектах. Если включишь все правила, получишь 500 ошибок и никто не будет слушать. Начни с критичных (SQL injection, XSS, credentials), потом постепенно добавляй остальное.

SAST vs DAST vs Pentesting

Частый вопрос: чем это отличается?

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

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

Pentesting — это ручная работа квалифицированного специалиста. Дорого, долго, но находит самые хитрые уязвимости.

Правильный подход: SAST в каждом push, DAST в staging перед release, pentesting перед выходом в production или если у тебя финтех/медтех.

Практические советы для внедрения

По опыту, вот что реально помогает:

Сначала настрой SAST для поиска критичных уязвимостей. Не пытайся поймать всё сразу — это убьёт мотивацию команды.

Интегрируй в merge request. Когда разработчик видит замечание прямо в коде, он сразу его исправляет. Если отправлять отчёты в отдельный чат — забудут.

Обучи команду. Поставь у себя в Slack бота, который будет объяснять, почему это опасно и как исправить. Хотя бы для top-10 OWASP.

Регулярно обновляй rules. Новые уязвимости появляются постоянно. Если ты не обновил Semgrep пол года — пропустишь половину.

Не игнорируй findings. Я видел проекты, где в .semgrep.yml заблокирована половина правил потому что "генерируют false positives". Это не решение. Правильно настрой правила или уменьши sensitivity.

Когда SAST недостаточно

Честно? SAST ловит 60-70% типичных уязвимостей. Но есть вещи, которые он не видит:

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

Race conditions в многопоточном коде. SAST видит статику, не динамику.

Проблемы с конфигурацией. Если ты неправильно настроил права доступа в облаке — SAST не найдёт.

Уязвимости в зависимостях, которых нет в базе данных инструмента. Здесь помогает OWASP Dependency-Check и Snyk.

Для этого и нужна комбинация инструментов. SAST — первая линия защиты, но не последняя.


Вот, собственно, весь смысл. SAST приложений — это не волшебство, это просто инструмент, который автоматизирует то, что раньше проверяли вручную на code review. И работает отлично.

Если ты ещё не встроил SAST в свой pipeline — начни с Semgrep и GitHub Actions (или GitLab CI). За час настроишь, за день найдёшь первые проблемы.

Кстати, в Distiq мы тоже используем SAST правила в нашем AI code reviewer. Когда ты создаёшь pull request, бот не только ловит стилистические проблемы, но и сканирует на безопасность — SQL injection, XSS, hardcoded credentials, всё как надо. Экономит время на code review и ловит то, что легко упустить человеку.

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

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

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

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