«У нас было два пакета findings SAST’а, семьдесят пять CVE с критичностью — Critical, пять дублей одной и той же CVE в разных сервисах, пол солонки false positive и целая россыпь уязвимостей всех сортов и расцветок: SQLi, XSS, SSRF, RCE, IDOR, утекшие секреты, misconfigs в Kubernetes, написанные человеком, который явно не планировал дожить до аудита.

Кроме того, у нас были изменения, сгенерированные AI-ассистентами, забытые исключения в проверках доступа, временные обходные решения, давно ставшие частью архитектуры, два отчета пентеста, тысячи задач и дашборд, который краснел так, будто видел все наши будущие инциденты сразу.

Не то чтобы это был необходимый запас для управления безопасностью приложений, но если уж ты решил строить ASPM через агрегацию всего подряд, рано или поздно ты оказываешься именно в такой машине — на полной скорости, без карты, с разработчиками на заднем сидении, которые только и спрашивают: “Что из этого реально надо исправлять?”». 

Всем привет! Меня зовут Артем Пузанков, я руководитель отдела консалтинга безопасной разработки в Бастионе. Сегодня хотелось бы порефлексировать с вами про управление состоянием безопасности приложений, ASPM, AI-generated код и AppSec.

Эта статья о том, почему будущее ASPM не в том, чтобы собрать все дефекты в «единое окно», а в том, чтобы сопоставить обнаруженные находки, проверить достижимость и отделить реальные угрозы от шума (читай технического долга).


Что вообще такое ASPM и зачем он нужен

Application Security Posture Management, или ASPM — это слой управления безопасностью приложений, который объединяет данные из разных AppSec-инструментов и помогает понять, что происходит с рисками по всему жизненному циклу разработки. 

Представьте: у команды есть несколько инструментов безопасности. Один читает код и ищет опасные конструкции, второй проверяет сторонние библиотеки, третий ищет ошибки в Kubernetes-манифестах, четвертый — пароли и токены, случайно оставленные в коде.

Каждый инструмент работает независимо и выдает свой список проблем. В итоге у команды три таблицы, два дашборда, пятьсот строк с находками и ни одного понятного ответа на вопрос «что вообще происходит и что чинить первым?».

ASPM необходим, чтобы превратить разрозненные результаты AppSec-инструментов в управляемый процесс. В хорошей реализации ASPM становится мостом между командой безопасности и разработчиками, помогая довести проблему до исправления: связать находку с репозиторием, компонентом, владельцем, задачей в трекере и статусом исправления.

Быстрые победы

Но к ASPM обычно приходят не сразу. Поначалу мне, как и многим, казалось, что главный корень проблем в AppSec довольно прост: мы проверяем не всё, что должны. Есть инструмент — видим проблему, нет инструмента — живем в счастливом неведении. Нет SAST — не видим дефекты в коде, нет SCA — не понимаем, какие уязвимые зависимости тащим в продукт, нет DAST — не проверяем приложение снаружи, нет secrets scanning — рано или поздно кто-нибудь закоммитит токен, ну и так далее. Логика простая — чем больше проверок, тем больше находок, тем безопаснее разработка.

В итоге компании начинают внедрять всё сразу: SAST, SCA, DAST, container scanning, IaC scanning, secrets scanning. Добавляют всё это в CI/CD одной пилотной команды, заводят SLA, настраивают тикеты, масштабируют на всю компанию и делают красивые дашборды для топ-менеджмента. 

На презентациях выглядит красиво и убедительно. Видно, какие приложения сканируются, где больше всего critical/high, какие команды исправляют быстро, а какие копят долг, где старые зависимости, где секреты, где небезопасные конфиги. Казалось бы, наконец-то, начинает складываться полная картина.

Первые недели после такого обычно проходят на энтузиазме: «Ура, мы построили AppSec/DevSecOps!». Все довольны, CISO рапортует наверх: «Теперь у нас управляемость». Разработка говорит: «Ладно, задач больше, но хотя бы понятно, откуда они берутся». Занавес, счастливый конец… а потом начинается следующий квартал. Дашборд постепенно становится всё краснее, а бэклог всё длиннее…

  • SAST находит possible SQL injection, критичность — high. Разработчик открывает код и видит, что потенциально опасный sink находится во внутренней batch-задаче и туда не попадает HTTP-запрос, нет внешнего параметра, а данные проходят через заранее контролируемый источник. Формально паттерн опасный, но реального пути от недоверенного ввода до SQL-запроса пока не видно.

  • SCA находит критическую CVE в библиотеке. Потом выясняется, что библиотека действительно есть в dependency tree, но уязвимая функция не вызывается, зависимость используется только в devtools и не попадает в боевую сборку. CVE остается CVE, но ее приоритет для конкретного приложения меняется.

  • DAST ничего не находит.

  • Container Security находит десятки CVE в базовом образе.

  • Secrets scanner «тревожится» о тестовом ключе из документации.

Формально всё работает: инструменты находят проблемы, тикеты создаются, дашборд то зеленеет, то снова краснеет. Но в какой-то момент разработка задает самый неприятный вопрос: «Что из этого надо исправить прямо сейчас, чтобы выкатить релиз?».

И вот именно тут мы понимаем, что общий список находок помогает увидеть масштаб проблемы, но никак не помогает ответить разработчику на его вопрос.

Быстрые победы закончились. Начинаем прозревать

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

А если findings тысячи? Команда AppSec очень быстро перестает помогать разработке принимать правильные решения и превращается в диспетчерскую службу по обработке алертов.

Здесь легко сделать неправильный вывод — проблема в сканерах. SAST фолзит, SCA спамит CVE, а разработчики не хотят исправлять уязвимости. Но сканеры не принимают решение за AppSec. Они не знают ваш продукт, не понимают всех его нюансов. Их задача проще — заметить подозрительное место и заалертить. Именно поэтому они часто перестраховываются. Ложное срабатывание — лишний разбор, раздражение разработчика и минус доверие к инструменту, что неприятно, но пережить можно. А вот пропущенная уязвимость, которая попала в прод и превратилась в инцидент… Поэтому сканер почти всегда следует логике: лучше лишний раз «прокричать», чем промолчать там, где мог быть реальный риск. Но это не значит, что шум необходимо бездумно пересылать разработке.

После первого скана начинается настоящая работа — не переслать алерт дальше, а понять, где он появился, почему инструмент среагировал, есть ли похожие ложные срабатывания, какие правила нужно докрутить, есть ли за ним реальная возможность эксплуатации, какой сервис затронут и что будет, если этим воспользоваться. И только после этого решать, это задача для разработки, технический долг или false positive, который нужно исключить «тонкой» настройкой инструмента.

Вот здесь ASPM и должен перестать быть просто «единым окном». Его задача — не просто сложить все алерты в один список, а помочь разобраться, что за ними стоит — реальная уязвимость или повторяющийся false positive. 

Если этого шага нет, AppSec просто маршрутизирует спам из инструментов в разработку. Инструменты что-то нашли, ASPM всё это красиво уложил, разработке прилетела задача. 

Мое мнение: мы слишком долго принимали наличие находки за наличие уязвимости. Инструменты показывают сигналы. Уязвимостью в практическом смысле они становятся только тогда, когда мы сопоставляем их между собой и с контекстом приложения. Но сигнал сам по себе редко отвечает на вопрос, насколько это опасно именно для нашего приложения. 

Именно здесь и нужна корреляция. Не в смысле «подтвердить статику динамикой», а в смысле «связать находку с проверяемыми фактами вокруг нее»: вызывается ли уязвимая функция, есть ли путь от входных данных до опасного вызова, попадает ли компонент в боевую сборку, доступен ли сервис извне, какие права у найденного секрета, кто владелец сервиса и насколько этот сервис критичен.

Поэтому сводить ASPM к «единому окну», куда сваливаются результаты всех сканеров, — значит, сильно недооценивать его пользу. В таком виде он помогает увидеть масштаб проблемы, но не всегда помогает принять решение. Настоящая ценность ASPM появляется на следующем шаге — когда он становится слоем корреляции и интерпретации: показывает, что из найденного похоже на реальный риск, что требует ручного разбора, что можно честно унести в технический долг.

В этот момент findings перестают быть россыпью отдельных алертов и начинают складываться в сценарии. SAST показывает опасную конструкцию, DAST подтверждает ее снаружи, runtime говорит, что код реально исполняется, service catalog показывает критичность сервиса — и перед нами уже не набор разрозненных сигналов, а конкретная угроза.

Это принципиально разные уровни зрелости. В первом случае мы просто складываем данные в одну систему, во втором — начинаем понимать смысл этих данных. ASPM полезен не самим фактом хранения находок, а тем, что помогает объяснить их приоритет. Один дефект блокирует релиз, другой остается в техническом долге, и у каждого решения есть понятная причина.

В SCA подобная мысль уже стала привычной. Если в проекте есть уязвимая библиотека — плохо, но это еще не означает, что уязвимость можно эксплуатировать. Важно не только то, что библиотека находится в dependency tree. Важно, используется ли уязвимая функция, можно ли до нее добраться из приложения, может ли пользователь повлиять на входные данные, есть ли этот код в production, доступен ли сервис извне и насколько он критичен.

Если же библиотека есть, но уязвимый участок никогда не вызывается, риск один, если эта же библиотека обрабатывает публичный API-запрос в платежном сервисе, риск совсем другой.

Тот же принцип нужен и для SAST. Потенциальная SQL injection в коде — гипотеза, достижимый путь от внешнего параметра до SQL-запроса — риск, а если этот путь подтвержден DAST/runtime — конкретный сценарий атаки.

Разработчику не нужна задача вида:

"Possible SQL Injection. Severity High"

Разработчику нужна задача другого уровня:

«В публичном endpoint /api/payment/search параметр query попадает в SQL-запрос без параметризации. Путь подтвержден статическим анализом и DAST-проверкой. Сервис работает в production и обрабатывает платежные данные. Исправить до релиза».

В первом случае AppSec приносит разработчику подозрение, а во втором — уязвимость с подтвержденным путем эксплуатации.

SAST нашел потенциальную command injection. API inventory показывает, что endpoint публичный. DAST смог дойти до этого endpoint. Runtime показывает, что соответствующая функция реально исполняется. Cloud показывает, что сервис доступен из интернета. Service catalog говорит, что сервис критичен и работает с клиентскими данными. Вот это риск.

А если SAST нашел похожий дефект в модуле внутреннего калькулятора дней отпуска, который не вызывается, не развернут в production и не имеет внешнего входа, это не значит, что проблему нужно забыть — ее необходимо правильно классифицировать: не как инцидент/блокер релиза, а как технический долг или задачу на рефакторинг.

Теперь поговорим про критичность. Одна из самых вредных привычек AppSec — называть всё «хай/критикал» уязвимостями. Сканер поставил High — значит, High, CVE критичная — значит, срочно. 

Проблема в том, что разработка быстро перестает этому верить. Если каждая потенциальная уязвимость приходит как срочная, команда начинает воспринимать безопасность как что-то фоновое. Если половина high findings не воспроизводится, доверие угасает. Если разработчик тратит день на разбор недостижимого дефекта, следующую настоящую уязвимость он тоже встретит скепсисом. Это опасный момент, потому что AppSec может иметь хорошие инструменты, красивые дашборды, но при этом потерять главное — доверие разработки. А без доверия ничего не работает.

«Повайбкодим?»

Ах да, куда же без «вайбкодинга» при помощи AI-ассистентов, который ускорил написание кода… и ускорил накопление долга :)

На этом фоне появляется еще один вектор — AI в разработке. AI-ассистенты и агенты действительно ускоряют разработку: помогают писать шаблонный код, тесты, миграции, CRUD, документацию, рефакторить легаси, объяснять незнакомые участки кодовой базы — вообще всё. Но есть и побочный эффект: кода становится больше, merge requests становится больше, поверхность атаки растет, а вместе с ней растет и security debt.

Правда в том, что AI-ассистенты не знают контекст продукта, по крайней мере, пока. Они могут добавить новый endpoint и тем самым незаметно расширить поверхность атаки. Могут залогировать request body «для удобства отладки», не понимая, что там персональные данные. А могут предложить зависимость, которой вообще не существует — это называют package hallucination. Это не то же самое, что dependency confusion, но связь есть: выдуманное моделью имя пакета может стать удобной целью для злоумышленника, который зарегистрирует такой пакет в публичном репозитории и превратит ошибку автодополнения в риск цепочки поставки.

Если раньше команда выпускала 20 MR в неделю, а теперь с AI выпускает 40, то для команды разработки это выглядит как ускорение, но для AppSec — резкое увеличение потока (х2) проверок и находок, среди которых необходимо отличить реальную уязвимость от очередной подозрительной конструкции, проверить ее, объяснить и приоритизировать.

А затем всё это «добро» попадает в сканеры, далее — в ASPM, а если он умеет только агрегировать — backlog просто разрастается.

Именно поэтому AI-разработка делает ASPM еще более важным инструментом, поскольку старый подход «каждый finding — задача разработчику» в мире еще сильнее ускорившейся быстрой разработки окончательно разбивается. Код создается быстрее, чем AppSec успевает его вручную разбирать и поэтому безопасности снова приходится адаптироваться.

Фантазии

Теперь давайте пофантазируем о том, как AI в APSM мог бы упростить интерпретацию уязвимостей в сгенерированном коде.

Найденных дефектов у нас и так слишком много, так что фатальной ошибкой будет ожидать от AI еще одного списка уязвимостей. Мое мнение, что настоящая ценность AI в ASPM не в том, чтобы еще больше увеличить количество findings, а в том, чтобы помочь понять, какие из них связаны между собой, какие достижимы и что с ними делать.

Не хочется останавливаться на типовых сценариях, которые уже кто-то реализовал: AI объясняет уязвимый участок кода, AI предлагает исправление. Давайте помечтаем о большем.

1. AI может стать аналитическим слоем поверх ASPM

Например, SAST находит потенциальную SSRF: пользовательский параметр влияет на URL, по которому сервис делает исходящий запрос. Само по себе это еще не подтвержденная уязвимость. Но ASPM добавляет контекст: API inventory показывает, что endpoint опубликован наружу, DAST подтверждает внешний доступ.

Простая агрегация покажет несколько отдельных находок. AI-слой может связать их в гипотезу эксплуатации: внешний пользователь управляет параметром, параметр потенциально доходит до HTTP-клиента, значит, SSRF может быть достижимой.

Ключевое слово «может». Это не должно превращаться в «AI approved — значит, доказано». Гипотеза должна подтверждаться воспроизведением: data flow, call graph, DAST или другой проверяемой логикой. Иначе мы просто заменим шум сканеров на уверенный, но непроверенный вывод модели.

В этом месте AI помогает не генерировать еще один алерт, а превратить набор разрозненных находок в понятный сценарий атаки. Но без такого сопоставления ASPM остается бесполезным SIEM’ом для AppSec-дефектов, а не системой, которая помогает понять реальную опасность.

2. AI как переводчик между AppSec и разработкой

Понимаю, что похожая функциональность уже есть в некоторых продуктах.

Еще одна важная роль AI — переводить findings с языка сканера на язык человека.

Сканер может написать:

"CWE-89: Improper Neutralization of Special Elements used in an SQL Command"

И формально будет прав.

Но разработчик читает это и не видит задачи. Ему всё равно нужно самому открыть код, найти источник данных, пройти путь до SQL-запроса, понять, доступен ли endpoint извне, воспроизводится ли сценарий и что именно надо менять.

Нормальная задача должна выглядеть иначе:

«В методе searchPayments() параметр query из HTTP-запроса попадает в SQL-запрос через строковую конкатенацию. Endpoint /api/payment/search доступен извне и работает в production. DAST подтвердил, что параметром можно изменить структуру SQL-запроса. Исправление: заменить конкатенацию на параметризованный запрос».

Другое дело? В результате разработчик получает не High от SAST, а понятный путь исправления.

3. AI для умной дедупликации

Обычная дедупликация часто работает примитивно: одинаковый CWE, одинаковый файл, одинаковая строка, хэши. AI может группировать это не по одинаковому ID, а по смыслу.

Например:

«Это, вероятно, одна и та же уязвимость: небезопасная обработка параметра redirect_uri в authentication flow».

Так, ASPM перестает показывать пять разных «критичных находок» и показывает один риск с несколькими подтверждающими источниками.

4. AI для автоматизации триажа

Похожая функциональность уже есть в некоторых продуктах, на мой взгляд, один из самых практичных сценариев применения AI — AppSec-аналитики будут вас любить. 

Никто не говорит о полном доверии, но AI может помочь на первом уровне triage — не принять финальное решение, а разложить findings по корзинам: 

  • Needs human review — finding выглядит важным, но данных не хватает.

  • Technical debt — проблема реальная, но сейчас нет признаков достижимости.

  • False positive candidate — finding похож на ложное срабатывание.

5. AI для построения цепочки эксплуатации

Один из самых сильных сценариев — построение понятной цепочки эксплуатации.

Не просто:

"Command injection. Severity High"

А так:

«Внешний пользователь вызывает /api/export. Параметр format попадает в ReportService.generate(). Метод вызывает системную команду без проверки по списку разрешенных значений. Сервис работает в боевой среде и имеет доступ к хранилищу клиентских отчетов. Возможные последствия — чтение или изменение отчетов».

Это уже не обычный алерт от сканера, а описание того, как уязвимость может быть использована и к чему это приведет.

6. AI для построения эффективных метрик/статистики

Оставлю это для вас, как пищу для размышлений 🙂


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

А значит, меняются и требования к ASPM. Просто собрать результаты сканеров в одном месте уже мало, платформа должна помогать ответить на тот самый главный вопрос разработчика: «Что из этого надо исправить прямо сейчас, чтобы выкатить релиз?».

А что должен уметь ASPM следующего поколения?

ASPM следующего поколения должен не просто собирать findings и запускать инструменты, он должен помогать принимать решения.

ASPM 1.0

ASPM 2.0

Дефект = задача разработчику

Дефект = сигнал для анализа

Критичность — как определил сканер 

Оценка риска с учетом контекста приложения

Агрегация / дедупликация

Агрегация / дедупликация / корреляция

CVE в dependency tree

Достижимая уязвимая функция

Любой high/critical — исправлять немедленно 

Исправлять срочно только достижимый и подтвержденный high/critical

Разработчик разбирается самостоятельно

AppSec приносит подтвержденный сценарий эксплуатации

ASPM 2.0 — уже не просто агрегация, это условная доказательная модель риска, формула которой может выглядеть так:

Риск = дефект × достижимость × критичность сервиса × возможность эксплуатации × последствия

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

Что делать с остальными findings?

Важность корреляции и достижимости не означает, что все недостижимые дефекты можно отбросить и забыть. Отождествление вида «не достижимо = не важно» неприемлемо.

Недостижимый дефект сегодня может стать достижимым после новой фичи, рефакторинга или открытия внутреннего API наружу. Уязвимая зависимость, которая сейчас не используется, может начать использоваться через месяц, а у зависимости, которая сегодня кажется неэксплуатируемой, завтра может появиться рабочий эксплойт. Поэтому такие проблемы не исчезают. Они переходят в другую категорию — технический долг. Это не попытка замести проблему под ковер, а способ честно разнести срочные угрозы и плановую инженерную рутину.

Вывод

Будущее ASPM не в том, чтобы собрать все результаты сканирования в «единое окно», которое уже стало базой. Будущее в том, чтобы сопоставить эти результаты между собой и показать, какие из них действительно создают риск.

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

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

А если всё называется критичной уязвимостью, разработка перестает верить AppSec, а когда AppSec приходит не с очередным «хаем/критом», а с объяснением, почему именно эта проблема достижима, где она проявляется и чем может закончиться, разговор становится совсем другим.

Спасибо всем, кто дочитал статью до конца.

А теперь честно: разработчики у вас уже спрашивали с заднего сидения «что из этого реально чинить?», или машина пока только набирает скорость?


PURP — Telegram-канал, где кибербезопасность раскрывается с обеих сторон баррикад

t.me/purp_sec — инсайды и инсайты из мира этичного хакинга и бизнес-ориентированной защиты от специалистов Бастиона