Лет пять назад я был на code review. Обычный MR, казалось бы — добавили фичу авторизации через SMS. Я глянул, одобрил. Через месяц у нас украли базу клиентов через SQL-инъекцию в том самом месте. Стыдно? Очень.
С тех пор я не полагаюсь только на свои глаза. И на глаза коллег тоже. Человеческий фактор — это не оправдание, это реальность. Люди устают, отвлекаются, пропускают очевидное. Поэтому SAST — статический анализ безопасности кода — теперь обязательная часть моего пайплайна.
Что вообще такое SAST и чем отличается от DAST
SAST (Static Application Security Testing) анализирует исходный код, не запуская его. DAST (Dynamic Application Security Testing) — наоборот, работает с уже запущенным приложением, тыкая в него снаружи.
Аналогия простая. SAST — это когда строитель проверяет чертежи до стройки. DAST — когда пожарный инспектор приходит в готовое здание и ищет, где горит.
Оба подхода нужны. Но у SAST решений есть критическое преимущество: они находят проблемы на раннем этапе. Чем раньше найдена уязвимость, тем дешевле её исправить. Исправить баг на этапе разработки стоит копейки. На проде — тысячи долларов, репутационные потери и ночные деплои.
Хорошие SAST-инструменты умеют:
- Находить SQL-инъекции, XSS, CSRF
- Детектировать небезопасное использование криптографии
- Искать хардкоденные пароли и ключи API
- Проверять зависимости на известные CVE
Но есть нюанс. SAST не понимает, как код будет выполняться. Он видит текст. Поэтому бывают false positives — ложные срабатывания. Много их.
Типичные уязвимости, которые ловит SAST
SQL-инъекции
Классика. Пример на Python:
# Плохо — конкатенация строк
def get_user(user_id):
query = f"SELECT * FROM users WHERE id = {user_id}"
return db.execute(query)
# Хорошо — параметризованный запрос
def get_user(user_id):
query = "SELECT * FROM users WHERE id = ?"
return db.execute(query, (user_id,))
Любой SAST найдёт первый вариант и покажет на него пальцем. И правильно сделает.
XSS — межсайтовый скриптинг
Тоже боль. Особенно в SPA-приложениях.
// Плохо — вставка без санитизации
element.innerHTML = userInput;
// Хорошо — textContent или санитизация
element.textContent = userInput;
// или
element.innerHTML = DOMPurify.sanitize(userInput);
Хардкоденные секреты
Я видел это десятки раз. Коммитят AWS-ключи, API-токены, пароли от баз данных.
# Так делать нельзя, но все делают
API_KEY = "sk-live-51234567890abcdefghijklmnop"
DB_PASSWORD = "admin123" # да, реально такое было
SAST-сканеры умеют искать паттерны секретов. Regex по типичным форматам ключей — и готово. Но надёжнее использовать pre-commit хуки, которые блокируют такие коммиты локально.
Небезопасная десериализация
Это уже посложнее. Но критично.
# Опасно — pickle может выполнить произвольный код
import pickle
data = pickle.loads(user_input)
# Безопаснее — json для простых данных
import json
data = json.loads(user_input)
Pickle в Python — это бомба замедленного действия. Если нагрузить в него данные от пользователя, можно получить удалённое выполнение кода. SAST это знает.
Как выбрать SAST-инструмент
Рынок SAST решений сейчас огромен. Есть бесплатные open-source инструменты. Есть дорогие Enterprise-платформы за десятки тысяч долларов в год. Что выбрать?
Сразу скажу: бесплатное — не значит плохое. SonarQube Community Edition покрывает базовые потребности. Bandit для Python — отличный инструмент. ESLint с плагинами безопасности для JavaScript — must have.
Но есть проблема. Инструментов много, и все они разные. Каждый нужно настраивать, интегрировать, поддерживать. На одном проекте у нас было три разных сканера. Bandit для Python, ESLint для фронтенда, SpotBugs для Java. Конфигурация рассыпалась, результаты дублировались, разработчики ненавидели пайплайн.
По-хорошему, нужен один инструмент, который закрывает все языки в проекте. Или хотя бы унифицированный интерфейс для результатов.
Интеграция SAST в CI/CD pipeline
Вот тут многие ошибаются. Поднимают SonarQube, запускают сканирование раз в неделю руками. Это не работает. SAST должен быть частью каждого pull request.
Пример для GitLab CI:
stages:
- test
- security
sast:
stage: security
image: docker:stable
services:
- docker:dind
variables:
SAST_ANALYZER_IMAGE_TAG: latest
script:
- /analyzer run
artifacts:
reports:
sast: gl-sast-report.json
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
GitHub Actions — аналогично:
name: Security Scan
on: [pull_request]
jobs:
sast:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Run SAST
uses: github/super-linter@v4
env:
VALIDATE_ALL_CODEBASE: false
DEFAULT_BRANCH: main
Но просто запустить — мало. Нужно ещё и реагировать на результаты. Если сканер нашёл критическую уязвимость, MR не должен мёржиться автоматически. Guard rails — обязательная штука.
False positives — главная боль SAST
Вот честно: большая часть предупреждений SAST — шум. Особенно на легаси-проектах. Тысячи предупреждений, из которых реальных уязвимостей — единицы.
Что с этим делать?
Первое — настроить правила под свой стек. Если проект на Django, зачем проверять Spring-специфику? Отключить ненужное.
Второе — baseline. При первом запуске SAST найдёт сотни проблем. Не пытайтесь исправить всё сразу. Зафиксируйте baseline, и новые предупреждения показывайте только на новом коде.
Третье — приоритизация. Не все уязвимости одинаково важны. SQL-инъекция в публичном API — критично. Но хардкоднутый ключ от тестовой базы в закомментированном коде — low priority.
SAST vs Code Review — нужны оба
Есть мнение, что автоматизация заменит ревью. Нет. SAST решений много, а понимания бизнес-логики у них нет.
Сканер найдёт SQL-инъекцию. Но он не заметит, что вы разрешаете удалять чужие заказы, потому что проверка user_id стоит после запроса к базе. Это логическая ошибка, не паттерн уязвимости.
Поэтому идеальный сценарий: SAST работает как первый фильтр. Ловит очевидное, экономит время ревьюеров. А люди смотрят на архитектуру, бизнес-логику, неочевидные баги.
На одном проекте мы внедрили связку: SonarQube для SAST + обязательное ревью. Sonar находил 80% глупых ошибок. Ревьюеры сфокусировались на важном. Количество багов на проде упало втрое за квартал.
Что насчёт AI?
Современные AI-инструменты для code review умеют находить уязвимости лучше традиционных SAST. Они понимают контекст, видят паттерны, которые regex не заметит.
Я сейчас тестирую Distiq — российский AI-бот для code review. Он интегрируется в GitLab и GitHub, анализирует каждый MR, оставляет инлайн-комментарии. В отличие от классических SAST-сканеров, он меньше шумит и лучше понимает смысл кода. Для команд, которые не хотят тратить время на настройку SonarQube и борьбу с false positives — нормальный вариант. Внедряется за пару минут через webhook, серверы в РФ.
В общем, SAST — это база. Без него в 2024 году стыдно деплоить. Выбирайте инструмент, интегрируйте в пайплайн, не забывайте про ревью. И не повторяйте моих ошибок с SQL-инъекциями.
