Как мы в CodeScoring улучшили модель для поиска секретов

Как мы в CodeScoring улучшили модель для поиска секретов

Благодаря новой модели удалось повысить качество распознавания истинных секретов с 0.70 до 0.90 по метрике PR AUC.

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

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

Утечки секретов в публичные репозитории остаются серьёзной проблемой. Согласно исследованию GitGuardian, 4,6% публичных репозиториев содержат конфиденциальные данные, а 15% разработчиков хотя бы раз закоммитили токен. С ростом использования LLM и вайб-кодинга ситуация усугубляется: модели не всегда понимают, что код может попасть в открытый доступ, а удалить его оттуда — непросто.

Для поиска секретов применяют инструменты вроде Gitleaks, TruffleHog и Kingfisher. Они используют регулярные выражения, оценку энтропии, проверку в хранилищах вроде Vault. Однако такие подходы часто дают много ложноположительных срабатываний (False Positives), из-за чего настоящие угрозы теряются в шуме. Более того, агрессивные фильтры могут приводить к ложнонегативным (False Negatives) — когда реальные секреты пропускаются.

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

В октябре 2024 года мы запустили собственный модуль поиска секретов в CodeScoring. Первая версия использовала Gitleaks и модель XGBoost. Подход позволил сократить ложные срабатывания на 70%. Однако мы решили улучшить результаты, перейдя к более мощным архитектурам.

От бустинга к трансформерам

Задача поиска секретов сформулирована как бинарная классификация: является ли найденный фрагмент кода настоящим секретом?

Изначально использовался XGBoost с 30 признаками: длина и энтропия секрета, наличие ключевых слов (например, password), тип файла (test, example, config), структурные элементы (скобки, комментарии) и другие.

Такие признаки помогали отсеивать тестовые данные, но не передавали семантический контекст. Попытка добавить TF-IDF по токенам кода не дала значимого улучшения.

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

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

Сбор и подготовка данных

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

Однако данные оказались неидеальными: много дубликатов (один и тот же код в разных коммитах), а также противоречивые метки для одинаковых контекстов. После очистки из 97,5 тыс. примеров осталось около 48 тыс.

Чтобы увеличить разнообразие, применили аугментацию:

  • замена секретов по паттерну на сгенерированные аналоги;
  • подстановка реальных паролей из датасетов Password Strength Dataset и SecLists;
  • замена контекстов авторизации на аналогичные из PassFinder.

После аугментации получили 70 тыс. примеров. Часть из них позже отсеяли по результатам анализа ошибок модели.

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

Выборка несбалансирована: около 85% кандидатов — ложные срабатывания. Основная цель — не пропустить ни одного настоящего секрета, не перегружая аналитика ложными оповещениями.

Поэтому в качестве метрики выбрали PR AUC — площадь под кривой Precision-Recall. Она устойчива к дисбалансу и хорошо отражает способность модели ранжировать результаты: сначала показывать наиболее вероятные угрозы.

Подводные камни токенизации

Первоначально токенизатор обрабатывал контекст вокруг секрета (до 256 токенов). Однако при длинных фрагментах секрет мог не попасть в окно. Кроме того, токенизатор разбивал секреты на мелкие биграммы, что ухудшало понимание структуры.

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

Однако это снизило качество до 0.72 PR AUC. Анализ показал: ключевой контекст чаще находится до секрета, а не после. Мы перераспределили токены в пользу предшествующего контекста — и подняли метрику до 0.86.

Три вызова токенизатора стали узким местом. Мы попробовали объединить в один, используя return_offsets_mapping=True, но столкнулись с проблемой: первые символы секрета могли «сливаться» с последними символами контекста в один токен.

Например, строка 'SECRET 296866b2119ff5afbd84c4ee98dff791;/n' при сплошной токенизации теряла чёткое разделение между контекстом и секретом. При раздельной обработке — сохраняла.

Для решения добавили специальные токены до и после секрета:

'SECRET 296866b2119ff5afbd84c4ee98dff791;/n'

Это позволило точнее выделять секрет, но качество немного упало. Мы заметили, что в некоторых случаях секрет встречается дважды — один раз изолированно, другой — в слиянии с контекстом. Это натолкнуло на идею: использовать двойную токенизацию как аугментацию на уровне токенов.

Внедрение этого подхода в длинных контекстах позволило достичь 0.90 PR AUC.

Итоги

Переход с XGBoost на трансформерную архитектуру с учётом тонкой настройки токенизации повысил качество распознавания с 0.70 до 0.90 PR AUC.

Ключевым оказалось не просто применение современной модели, а глубокая работа с данными, аугментация и внимание к деталям токенизации. Особенно важно — как контекст до секрета влияет на предсказание.

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

Читать оригинал