Статья для тех, кто устал от того, что баги безопасности находят в продакшене, а не в разработке. SAST — это не просто очередной инструмент. Это способ ловить проблемы в коде ещё до того, как его запустить.
Я расскажу, как это работает на практике, какие уязвимости ловятся легко, какие — вообще не видны обычному code review, и как встроить SAST в свой pipeline за полчаса.
Что вообще такое SAST и зачем оно нужно
SAST расшифровывается как Static Application Security Testing. По-русски: статическое тестирование безопасности приложения. Инструмент анализирует исходный код без его запуска. Просто смотрит на текст программы и ищет опасные конструкции.
Представь, что SAST — это код ревьюер, который никогда не спит, не пропускает строчку и помнит все CVE за последние 10 лет.
По моему опыту, большинство команд до сих пор полагаются на ручной code review. Это работает, пока в команде 2-3 опытных разработчика с параноей. Но когда вы растёте, это становится узким местом. Человек не может в одну минуту проверить 500 строк кода на 50+ типов уязвимостей.
SAST решает ровно эту проблему. Автоматически. На каждый коммит. Без выходных.
Отличие SAST от DAST просто: SAST смотрит на код (Static), DAST запускает приложение и пробует его сломать (Dynamic). Нам нужны оба, но SAST ловит проблемы раньше.
Какие уязвимости ловит SAST
Не все баги безопасности видны статическому анализу. Но большинство известных проблем — видны. Вот самые частые.
SQL-injection — королева уязвимостей. Ловится почти всеми SAST-инструментами, потому что паттерн очень характерный.
# Плохо. SAST сразу вопит
user_id = request.args.get('id')
query = f"SELECT * FROM users WHERE id = {user_id}"
db.execute(query)
# Хорошо. SAST молчит
query = "SELECT * FROM users WHERE id = ?"
db.execute(query, (user_id,))
SAST видит, что вы конкатенируете переменную прямо в SQL-строку. Красный флаг.
XSS (Cross-Site Scripting) — когда вы выводите пользовательский input прямо в HTML без экранирования. SAST отслеживает, откуда берётся данные (источник — source) и куда они идут (сток — sink).
// Плохо
const username = req.query.name;
res.send(`<h1>Hello, ${username}</h1>`);
// Хорошо
const username = escapeHtml(req.query.name);
res.send(`<h1>Hello, ${username}</h1>`);
Command injection — когда вы передаёте пользовательский ввод в системные команды.
# Плохо
filename = request.form.get('file')
os.system(f"rm /uploads/{filename}")
# Хорошо
filename = request.form.get('file')
os.remove(f"/uploads/{sanitize(filename)}")
Hardcoded credentials — когда пароль или API-ключ прямо в коде. SAST просто ищет регулярные выражения типа password =, api_key =, secret =.
# Плохо
DB_PASSWORD = "super_secret_123"
AWS_KEY = "AKIAIOSFODNN7EXAMPLE"
# Хорошо
DB_PASSWORD = os.getenv('DB_PASSWORD')
AWS_KEY = os.getenv('AWS_ACCESS_KEY_ID')
Insecure deserialization — когда вы десериализуете объекты из ненадёжного источника. Особенно опасно в Java и Python.
# Плохо в Python
import pickle
data = request.data
obj = pickle.loads(data) # Это как впустить вредонос в дом
# Хорошо
import json
data = request.data
obj = json.loads(data) # JSON намного безопаснее
Weak cryptography — когда используется MD5, SHA1 или другие устаревшие алгоритмы.
# Плохо
import hashlib
hashed = hashlib.md5(password.encode()).hexdigest()
# Хорошо
from argon2 import PasswordHasher
ph = PasswordHasher()
hashed = ph.hash(password)
SAST находит эти проблемы потому что ищет конкретные паттерны и отслеживает потоки данных. Чем более специализированный инструмент, тем больше уязвимостей он ловит.
Как SAST работает изнутри
Инструмент не просто делает текстовый поиск. Это намного сложнее.
Сначала парсер разбирает код в абстрактное синтаксическое дерево (AST). Это представление кода в виде структуры, которая показывает логику программы.
Потом анализатор отслеживает потоки данных (data flow analysis). Откуда берётся переменная? Где она используется? Может ли туда попасть недоверенный ввод?
И наконец — проверка паттернов. Если опасные данные попадают в опасное место, это уязвимость.
Звучит просто, но на практике это сложная задача. Нужно учитывать тысячи линий кода, библиотеки, условную логику.
Вот почему хорошие SAST-инструменты долго анализируют большие проекты. На одном проекте в Яндексе первый скан занял 40 минут на кодовой базе в 2 млн строк. Но потом инкрементальные сканы были за минуту.
SAST в CI/CD pipeline: практический пример
Встроить SAST в pipeline — это не сложно. Вот как это выглядит на примере GitLab.
# .gitlab-ci.yml
stages:
- scan
- build
- test
sast:
stage: scan
image: returntocorp/semgrep
script:
- semgrep --config=p/security-audit --json -o sast-report.json .
artifacts:
reports:
sast: sast-report.json
allow_failure: false
Что здесь происходит:
- На этапе
scan(до сборки приложения) запускается инструмент Semgrep - Он анализирует весь код в репозитории
- Результаты пишутся в JSON
- GitLab читает этот JSON и показывает уязвимости прямо в MR
Если найдена уязвимость с высокой серьёзностью, pipeline падает (allow_failure: false). Код не сливается, пока проблема не решена.
Для GitHub это выглядит похоже:
# .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/security-audit
p/owasp-top-ten
Я рекомендую комбинировать несколько инструментов. Один ловит что-то, другой — другое.
Популярные SAST-инструменты для разных языков
Для Python:
- Bandit — ловит типичные ошибки (hardcoded secrets, небезопасные функции)
- Semgrep — более мощный, кастомные правила
- SonarQube — полный анализ качества и безопасности
# Быстрый скан Bandit
bandit -r . -f json -o report.json
Для JavaScript/TypeScript:
- ESLint с плагинами безопасности
- SonarQube
- Snyk — находит уязвимости в зависимостях
npm install --save-dev eslint eslint-plugin-security
npx eslint . --ext .js,.ts
Для Java:
- SpotBugs — находит потенциальные баги
- Checkmarx — платный, но мощный
- SonarQube
Универсальные:
- Semgrep — работает с 20+ языками, кастомные правила
- SonarQube — полная платформа анализа
- Checkmarx — платный, но очень точный
На практике большинство команд используют комбинацию инструментов: что-то быстрое в CI/CD (Bandit, ESLint) и что-то более глубокое в отдельном сканировании (SonarQube, Checkmarx).
Когда SAST не помогает и где ошибаются команды
Честно? SAST — не панацея. Есть уязвимости, которые он не видит.
Business logic bugs. Если в коде нет технической ошибки, но логика работает неправильно — SAST не поможет. Пример: система скидок, которая считает скидку неправильно. Это не уязвимость, это баг логики.
Конфигурационные проблемы. Если сервер неправильно настроен (открыты все порты, слабые сертификаты), SAST это не видит. Это работа DevSecOps.
Проблемы с зависимостями. Если в npm-пакете уязвимость, SAST её не найдёт в вашем коде. Нужен отдельный инструмент — Snyk, npm audit, Dependabot.
Частые ошибки при внедрении SAST:
Включили инструмент, он нашёл 5000 проблем, и все начали их игнорировать. Не делайте так. Начните с малого: включите только критичные и высокие уровни серьёзности. Потом постепенно добавляйте.
Настроили SAST, но не настроили в pipeline. Инструмент работает локально, но результаты не видны в MR. Всё впустую. Интегрируйте в CI/CD.
Не обновляют правила. SAST-инструменты выпускают обновления с новыми CVE. Если не обновляться, через год инструмент будет слепой.
SAST vs DAST vs Manual Code Review
Разные подходы ловят разные проблемы. Идеально использовать все три.
SAST ловит проблемы в коде на статическом уровне. Быстро, дёшево, в разработке.
DAST запускает приложение и пробует его сломать. Ловит проблемы в runtime, конфигурацию, логику. Но медленнее и требует рабочего приложения.
Manual code review ловит бизнес-логику и архитектурные проблемы, которые не видны инструментам.
На практике: SAST в каждом MR (быстро), DAST перед релизом (детально), code review для всех MR (качество). Так работают серьёзные компании.
Как внедрить SAST в существующий проект
Если у вас уже есть проект с кодом, и вы хотите внедрить SAST, не делайте это резко.
Шаг 1: Выберите инструмент. Для быстрого старта возьмите Semgrep или SonarQube Community.
Шаг 2: Запустите первый скан на всём коде. Будет много ошибок. Это нормально.
Шаг 3: Не пытайтесь исправить всё сразу. Отключите низкоприоритетные проверки, оставьте только критичные и высокие.
Шаг 4: Интегрируйте в CI/CD так, чтобы pipeline падал только на новые уязвимости, а не на старые. Большинство инструментов это поддерживают (baseline, incremental scan).
# Пример: падаем только на новые проблемы
semgrep:
script:
- semgrep --config=p/security-audit --baseline=main . || true
Шаг 5: Постепенно расширяйте проверки. Каждый спринт добавляйте новые правила.
На одном проекте мы внедрили Semgrep за неделю и за месяц исправили 150 проблем. Ничего критичного не нашли, но много мелких уязвимостей вроде hardcoded credentials и небезопасных функций.
Как выбрать SAST-инструмент для вашей компании
Главные критерии:
Поддержка языков. Если вы пишете на Kotlin, убедитесь, что инструмент его поддерживает.
Скорость. SAST не должен замедлять разработку. Если скан 10 минут — это проблема.
Точность. Много ложных срабатываний = разработчики будут игнорировать результаты.
Интеграция с вашей платформой. GitLab, GitHub, GitVerse — есть ли встроенная поддержка?
Цена. Open source (Semgrep, SpotBugs) бесплатно, но требуют настройки. Платные (Checkmarx, SonarQube) дороже, но проще.
Для российских компаний важно: данные остаются на вашей инфраструктуре или уходят в облако? Если в облако — это может быть проблемой по закону.
Итог: SAST тестирование — это не опциональная фишка для больших компаний. Это базовый уровень безопасности, который должен быть в каждом проекте. Инструменты есть бесплатные, настройка занимает час-два.
