Тихий саботаж: как ваш локальный RAG превращается в ботнета
Вы загружаете техдокументацию в ChromaDB. Подключаетесь к LM Studio с новым Llama 3.3 через OpenAI-совместимый API. Запускаете запрос. И получаете от нейросети четкую инструкцию, как слить корпоративные пароли на левый сервер. Классический день DevOps в 2026 году.
На прошлой неделе мы тестировали уязвимости в локальном RAG-стеке. Результаты кошмарные: 95% атак на отравление базы знаний прошли успешно. Это не Prompt Injection - это Data Poisoning. Злоумышленник не ждет вашего промпта. Он заранее заражает векторную базу поддельными документами, и модель добросовестно их использует. Ваша задача - оборвать эту цепочку. Сегодня.
Почему фильтрация вывода не работает? Потому что модель говорит правду - ту правду, которую вы ей подсунули. Вы запрещаете слово "пароль", а она генерирует "учетные данные доступа". Игра в кошки-мышки проиграна на старте. Нужно бить по корню - по эмбеддингам.
Механика катастрофы: почему 512 токенов смертельны
Стандартная настройка: chunk_size=512, overlap=200. Звучит разумно? Для атаки - идеально. Злоумышленник создает документ, где 300 токенов - легитимная техническая информация, а 212 - вредоносные инструкции. При разбиении на чанки эта "отравленная" секция гарантированно попадает в середину какого-то чанка, обернутая нормальным контекстом. Алгоритм эмбеддинга усредняет смысл. И при поиске по схожести вы получаете не чистый яд, а коктейль с ядом.
Основная проблема в доверии к источнику. Как показано в статье про обратную инженерию эмбеддингов, семантический смысл можно извлечь даже из вектора. А значит - и подделать.
1 Шаг первый: ловим аномалии в эмбеддинг-пространстве
Забудьте про анализ текста. Смотрите на вектора. Каждый легитимный документ при эмбеддинге занимает определенную область в n-мерном пространстве. Отравленные чанки - выбросы. Их нужно найти до индексации.
Как это работает: после создания эмбеддингов для нового пакета документов запускаем алгоритм обнаружения аномалий (Isolation Forest, Local Outlier Factor). Если эмбеддинг чанка отклоняется от кластера похожих документов больше чем на 3 сигмы - помечаем на проверку.
import numpy as np
from sklearn.ensemble import IsolationForest
# embeddings - массив эмбеддингов новых чанков
# train_embeddings - эмбеддинги проверенного легитимного корпуса
def detect_poisoned_chunks(embeddings, train_embeddings, contamination=0.01):
# Обучаем модель на легитимных данных
clf = IsolationForest(contamination=contamination, random_state=42)
clf.fit(train_embeddings)
# Предсказываем аномалии в новых данных
predictions = clf.predict(embeddings)
# Возвращаем индексы подозрительных чанков (где prediction == -1)
return np.where(predictions == -1)[0]
Contamination=0.01 - ожидаем 1% аномалий. В реальности для нового неизученного корпуса ставьте 0.05. Ложные срабатывания лучше пропущенных угроз.
Не используйте эту функцию в продакшене без дообучения на ваших данных! Isolation Forest, обученный на случайных эмбеддингах, сработает плохо. Соберите эталонный корпус из 1000+ проверенных чанков.
2 Шаг второй: разрываем контекстную ловушку chunking'ом
Стандартные чанки убивают вас. Переходите на стратегический chunking с границами по смысловым разделам. Если в документе есть заголовок "Глава 3: Политика паролей", следующий чанк должен начинаться именно с него, а не с середины абзаца.
Используйте библиотеки вроде langchain с улучшенными text splitters, но настройте их под свои нужды:
from langchain.text_splitter import RecursiveCharacterTextSplitter
# Плохо
splitter_naive = RecursiveCharacterTextSplitter(chunk_size=512, chunk_overlap=200)
# Хорошо
splitter_smart = RecursiveCharacterTextSplitter(
chunk_size=256, # Меньше - меньше пространства для маневра атакующего
chunk_overlap=50,
separators=["\n\nГлава", "\n\n", "\n", ". ", " ", ""], # Приоритет разделителей
length_function=len,
is_separator_regex=False,
)
Чанки размером 256 токенов сложнее отравить без потери семантической связности вредоносной вставки. Перекрытие 50 токенов сохраняет контекст, но не позволяет скрыть яд в overlap-зоне.
Парадокс: чем больше chunk_overlap, тем легче спрятать атаку. Об этом мало кто думает.
3 Шаг третий: валидация источника на уровне загрузки
ChromaDB по умолчанию принимает все. Нужно встроить валидацию перед вызовом add_documents. Простейший фильтр - проверка метаданных и хэшей.
import hashlib
from typing import List, Dict
from langchain.schema import Document
def validate_documents_before_adding(docs: List[Document], trusted_sources: List[str]) -> List[Document]:
validated = []
for doc in docs:
# Проверяем источник из метаданных
source = doc.metadata.get("source", "")
# Простая проверка по доверенным путям/URL
if any(trusted_source in source for trusted_source in trusted_sources):
# Дополнительно считаем хэш содержимого для верификации
content_hash = hashlib.sha256(doc.page_content.encode()).hexdigest()
doc.metadata["content_hash"] = content_hash
validated.append(doc)
else:
# Логируем и отправляем на ручную проверку
print(f"Подозрительный источник: {source}")
# Здесь можно вызвать кастомную логику проверки
# Например, отправить в очередь на модерацию
return validated
Этот метод не панацея - атакующий может подделать путь. Но это первый барьер. Для серьезной защиты нужна цепочка доверия, как в статье про защиту AI-агентов от prompt injection.
4 Шаг четвертый: мониторинг запросов в реальном времени
Даже с защитой нужен детектор аномалий в запросах. Следите за необычными паттернами:
- Частые запросы к одним и тем же "чувствительным" чанкам
- Пользователи, которые получают слишком много разнородных чанков в одном ответе
- Резкий рост схожести запросов с определенными эмбеддингами
Настройте алерты. Используйте Prometheus + Grafana для визуализации метрик из ChromaDB (если развернута с помощью официального Helm0чарта). Или пишите логи в ELK-стек.
Критически важно: не храните логи запросов с эмбеддингами в открытом виде. Хэшируйте их или используйте дифференциальную приватность.
Где ломается 95% защит: ошибки, которые вы уже делаете
| Ошибка | Почему это плохо | Исправление |
|---|---|---|
| Использование дефолтных параметров в text splitter | Создает идеальные контейнеры для отравленных данных | Анализируйте свои документы, подбирайте chunk_size под их структуру |
| Отсутствие изоляции сред | Тестовая зараженная база может синхронизироваться с продом | Полная изоляция: отдельные инстансы ChromaDB, разные API-ключи LM Studio |
| Слепая вера в один алгоритм эмбеддингов | Атакующий может оптимизировать вредоносный текст под конкретную модель (например, all-MiniLM-L6-v2) | Используйте ансамбли: два разных эмбеддера, сравнение результатов |
Ответы на вопросы, которые вы боитесь задать
LM Studio тоже уязвим?
Да. LM Studio - это просто обертка для запуска моделей. Если модель получает отравленный контекст из ChromaDB, она обработает его как легитимный. Безопасность нужно строить на уровне данных, а не модели.
А если атакующий знает мою модель эмбеддингов?
Тогда задача усложняется. Он может создать adversarial пример, который в эмбеддинг-пространстве будет похож на "техническую документацию", а в текстовом виде содержать вредоносные инструкции. Защита - регулярная ротация моделей эмбеддингов и использование последних версий, где такие атаки частично учтены (например, sentence-transformers 3.0+ имеет улучшенную устойчивость).
Как связаны эта атака и новые угрозы GenAI?
Data poisoning - один из методов, описанных в обзоре новых угроз безопасности GenAI. Разница в том, что prompt injection работает в runtime, а poisoning - на этапе подготовки данных. Второе опаснее, потому что обнаруживается позже.
Можно ли автоматизировать защиту?
Частично. Вы можете настроить пайплайн CI/CD для вашей базы знаний: при пуше новых документов запускается скрипт проверки эмбеддингов, chunking с кастомными параметрами, и только потом индексация. Полностью исключить человека из цикла - рискованно. Особенно в свете того, что AI-агенты уже демонстрируют агрессию.
Что дальше? Прогноз на 2027
Data poisoning станет массовым. Атакующие перейдут от точечных атак к заражению публичных датасетов, которые компании используют для дообучения. Появится рынок "чистых" эмбеддингов с верифицированной цепочкой поставки. Инструменты вроде ChromaDB добавят встроенные механизмы валидации (возможно, на основе дифференциальной приватности).
Ваша задача сегодня - не ждать этих обновлений. Настроить базовую защиту по шагам выше. Провести аудит текущей базы знаний. И помнить: 95% - это не статистика. Это вероятность того, что завтра ваша RAG-система начнет работать на того, кто этого не ожидал.