Files
progcode/editorial/agent-rewrites/097.json
T

8 lines
23 KiB
JSON
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{
"index": 97,
"slug": "editorial-2025-04-field-ai-data-privacy",
"title": "Почему замаскированный лог всё ещё нельзя отправлять в AI-инструмент",
"excerpt": "Разбираем синтетический кейс: как отделить класс данных, условия сервиса, сетевой маршрут и полномочия, а затем воспроизвести безопасную остановку без внешнего запроса.",
"contentHtml": "<p>Инженер копирует в AI-чат фрагмент ошибки, заменяет имя на <code>[redacted]</code> и ждёт подсказку. На вид в тексте больше нет персональных данных. Но рядом могут остаться время операции, редкий тип события, идентификатор запроса, структура платежа или сочетание полей, по которому запись легко узнать. А сама поверхность может добавить к запросу историю диалога, файл проекта, поиск или другой контекст.</p>\n<p>Симптом здесь не в том, что AI дал плохой ответ. Проблема обнаруживается позже: команда не может доказать, какой фрагмент ушёл, к какому сервису, на каких условиях и с чьего разрешения. Цена такой неопределённости — повторная инвентаризация, остановка полезного сценария и риск раскрытия персональных, финансовых или служебных данных. Маскирование уменьшает содержимое, но не превращает его автоматически в разрешённый контекст.</p>\n<h2>Кейс: «это всего один лог»</h2>\n<p>Рассмотрим учебную ситуацию. Разработчик хочет показать ассистенту форму production-события и один тикет, чтобы найти причину повторной ошибки. В статье нет настоящего лога, клиента, секрета, идентификатора, файла или сетевого адреса. Вместо них используются фиксированные строки с пометкой <code>synthetic</code>. Это позволяет проверить границу решения, не передавая наружу даже учебный payload.</p>\n<p>Первое ложное упрощение звучит так: «имена удалены, значит данные обезличены». Для этого вывода нужно отдельно проверить остаточную структуру и риск повторной идентификации. Второе: «у нас корпоративный тариф, значит разрешён любой экран». У одного поставщика могут отличаться web-чат, расширение редактора, API, поиск и агент с доступом к репозиторию. Третье: «домен разрешён firewall, значит передачу одобрили». Сеть отвечает за маршрут, а не за классификацию записи и права человека.</p>\n<h2>Четыре независимых вопроса</h2>\n<p>Перед отправкой полезно описывать не строку, а одну единицу контекста. У неё есть идентификатор, версия, владелец, класс, назначение, destination, requester и срок действия решения. Если поле неизвестно, его нельзя заполнять оптимистичным значением <code>public</code>. Нулевое знание должно вести к остановке.</p>\n<div class=\"table-scroll\"><table><caption>Что проверяет каждый слой решения</caption><thead><tr><th scope=\"col\">Слой</th><th scope=\"col\">Вопрос</th><th scope=\"col\">Минимальное доказательство</th><th scope=\"col\">Чего он не доказывает</th></tr></thead><tbody><tr><td>Класс</td><td>Что представляет запись после преобразования?</td><td>Версия классификации, owner и описание остаточной структуры</td><td>Условия хранения у поставщика и право на egress</td></tr><tr><td>Policy и contract</td><td>Допустим ли этот класс для выбранной surface?</td><td>Документ и версия условий для конкретного plan, региона и функции</td><td>Что фактически попало в запрос и кто его отправляет</td></tr><tr><td>Маршрут</td><td>Куда может уйти запрос и дополнительный context?</td><td>Surface, destination, proxy или gateway и техническое правило</td><td>Безопасность payload и полномочия requester</td></tr><tr><td>Полномочия</td><td>Кто может использовать именно эту запись сейчас?</td><td>Scoped approval: record, цель, requester, issuer и expiry</td><td>Разрешение всех будущих фрагментов и интерфейсов</td></tr></tbody></table></div>\n<p>Эти слои пересекаются в одной операции, но не заменяют друг друга. Даже проверенный договор не классифицирует конкретный лог. Даже одобренный класс не открывает неизвестный endpoint. Даже доступ к AI-инструменту не означает право раскрывать все данные, которые видит пользователь. Итоговое решение должно ссылаться на все четыре слоя или закрывать путь.</p>\n<h2>Почему маскирование не равно анонимизации</h2>\n<p>Redaction — это преобразование значения. Классификация отвечает на другой вопрос: какой риск остаётся у результата. Например, замена номера клиента на <code>[redacted]</code> не скрывает редкую последовательность действий, точное время и вид операции. Небольшой набор таких признаков может сузить поиск до одной записи. Поэтому в карточке нужно фиксировать не только удалённые поля, но и то, что осталось.</p>\n<p>Не следует и автоматически называть результат анонимным. Статья не предлагает юридический тест анонимизации: его критерии зависят от применимого права, целей обработки и возможностей сопоставления. Инженерная граница проще: пока владелец данных не подтвердил класс и допустимость, фрагмент не отправляется. Для отладки лучше начать с заранее подготовленного примера, схемы события или локального fixture, если именно они отвечают на вопрос.</p>\n<h2>Почему разрешённая сеть не даёт права на данные</h2>\n<figure><img src=\"/assets/editorial/2025/ai-data-privacy-2025-egress-review.svg\" alt=\"Схема проверки AI-контекста: запись и класс, условия сервиса, маршрут, полномочия и финальное решение; любой несовпадающий факт ведёт к остановке без внешней отправки\" loading=\"lazy\" /><figcaption>Схема разделяет evidence и действие: положительный результат означает готовность к отдельному согласованному workflow, а не выполненный внешний запрос.</figcaption></figure>\n<p>Сетевой allow-list полезен, но его область действия узкая. Он может разрешить соединение с определённым destination или направить его через прокси. Он не анализирует смысл каждого поля, не знает срок действия approval и не решает, разрешена ли выбранная surface условиями организации. TLS подтверждает защищённость канала от подмены при передаче, но не делает саму передачу допустимой.</p>\n<p>Такой разрыв хорошо согласуется с принципом zero trust из NIST SP 800-207: доверие не должно следовать только из сетевого расположения, а authentication и authorization — отдельные функции перед доступом к ресурсу. Для AI-review это означает: факт «с рабочего ноутбука домен открывается» нельзя использовать как замену проверке requester, record и назначения.</p>\n<h2>Воспроизводимый fail-closed пример</h2>\n<p>Ниже — автономный Node.js-фрагмент. Он создаёт две синтетические карточки в памяти, проверяет класс, destination, requester, approval и срок, а затем печатает решение. Код не читает файлы, не строит prompt и не выполняет HTTP-запрос. Его можно сохранить в временный файл и запустить командой из блока; ожидаемые строки показывают только поведение локального правила.</p>\n<pre><code>node --input-type=module &lt;&lt;'NODE'\nconst policy = {\n allowedClass: 'synthetic-public-summary',\n destination: 'fixed-ai-review-boundary',\n requester: 'synthetic-engineering-read',\n issuer: 'synthetic-data-steward',\n now: '2025-04-19T12:00:00Z',\n};\n\nfunction decide(item) {\n const fields = ['recordId', 'dataClass', 'destination', 'requester', 'approvedBy', 'expiresAt'];\n if (fields.some((field) =&gt; typeof item[field] !== 'string' || item[field] === '')) {\n return { decision: 'stop', reason: 'incomplete-record' };\n }\n if (item.dataClass !== policy.allowedClass) {\n return { decision: 'stop', reason: 'class-not-allowed' };\n }\n if (item.destination !== policy.destination || item.requester !== policy.requester) {\n return { decision: 'stop', reason: 'scope-mismatch' };\n }\n if (item.approvedBy !== policy.issuer || Date.parse(item.expiresAt) &lt;= Date.parse(policy.now)) {\n return { decision: 'stop', reason: 'approval-missing-or-expired' };\n }\n return { decision: 'review-ready', externalCall: false };\n}\n\nconst restricted = {\n recordId: 'synthetic-log-shape-v1',\n dataClass: 'synthetic-restricted',\n destination: policy.destination,\n requester: policy.requester,\n approvedBy: policy.issuer,\n expiresAt: '2025-04-20T00:00:00Z',\n};\nconst allowed = { ...restricted, recordId: 'synthetic-public-summary-v1', dataClass: policy.allowedClass };\n\nconsole.log(decide(restricted)); // { decision: 'stop', reason: 'class-not-allowed' }\nconsole.log(decide(allowed)); // { decision: 'review-ready', externalCall: false }\nNODE</code></pre>\n<p>Важен не сам набор строк, а порядок. Запрет класса срабатывает до использования approval как исключения. Для допустимой учебной карточки результат означает лишь «можно передать evidence в следующий согласованный шаг». Поле <code>externalCall: false</code> намеренно подтверждает, что программа не отправляет ничего наружу. В рабочем сервисе вместо строк понадобятся реальные адаптеры к каталогу данных, identity-провайдеру и policy-as-code, но они не должны менять fail-closed смысл: неизвестное условие не становится разрешением.</p>\n<h2>Разбор симптома по цепочке</h2>\n<div class=\"table-scroll\"><table><caption>Диагностика перед AI-review</caption><thead><tr><th scope=\"col\">Наблюдение</th><th scope=\"col\">Гипотеза</th><th scope=\"col\">Проверка</th><th scope=\"col\">Действие</th></tr></thead><tbody><tr><td>Имена удалены</td><td>Остаточная структура безопасна</td><td>Сверить class после преобразования, редкие поля и owner</td><td>При неизвестном классе остановить передачу</td></tr><tr><td>Продукт разрешён</td><td>Все его поверхности одинаковы</td><td>Назвать точные feature, plan, region и destination</td><td>Сверить условия именно этой surface</td></tr><tr><td>Firewall пропускает домен</td><td>Технический egress равен праву на данные</td><td>Сопоставить route с class и policy</td><td>Не использовать сеть как approval</td></tr><tr><td>Есть согласование команды</td><td>Оно покрывает любой контекст</td><td>Проверить record, цель, requester, issuer и expiry</td><td>Запросить scoped решение или остаться локально</td></tr><tr><td>Ассистент ответил</td><td>Внешнего раскрытия не было</td><td>Проверить outbound request, дополнительные context sources и retention terms</td><td>Считать результат недоказанным до проверки маршрута</td></tr></tbody></table></div>\n<p>Таблица нужна не для бюрократии. Она не даёт одному удачному факту закрыть соседний вопрос. Если команда не может назвать destination, это отдельный пробел маршрута. Если destination известен, но нет scoped approval, это отдельный пробел полномочий. Такой разбор уменьшает спор о бренде инструмента и показывает, кому адресовать следующий вопрос.</p>\n<h2>Короткий pre-flight review</h2>\n<ol><li><strong>Зафиксируйте запись.</strong> Укажите record ID, версию, владельца и минимальную цель. Не помещайте исходный payload в заявку на согласование.</li><li><strong>Назначьте класс.</strong> Проверьте результат после маскирования и сохраните правило, по которому он классифицирован. Не называйте неизвестное значение public.</li><li><strong>Сверьте условия сервиса.</strong> Откройте документ для конкретной поверхности, тарифа, региона и функции. Отдельно проверьте retention, обучение и подключённый поиск, если они относятся к вашему решению.</li><li><strong>Опишите маршрут.</strong> Запишите destination, прокси, gateway, расширение и дополнительные источники context. Убедитесь, что технический путь разрешён для этого класса.</li><li><strong>Проверьте полномочия.</strong> Сопоставьте requester, record, цель, issuer и expiry. Доступ к инструменту не расширяйте до доступа ко всем данным пользователя.</li><li><strong>Прогоните отрицательный тест.</strong> Подставьте запрещённый класс, истёкший срок и другой destination. Во всех случаях ожидайте stop, причину и отсутствие внешнего вызова.</li><li><strong>Передайте только решение.</strong> При полном evidence отправьте карточку в согласованный workflow. При пробеле оставьте данные локально и назначьте владельца вопроса.</li></ol>\n<h2>Ограничения применимости</h2>\n<p>Учебный код не является DLP, системой классификации или юридическим заключением. Он не обнаруживает PII, не проверяет реальный endpoint, не знает договор конкретного поставщика, не измеряет retention, не контролирует права в identity-системе и не доказывает, что vendor сделал после получения запроса. Synthetic labels в примере не описывают ни одну настоящую организацию.</p>\n<p>Источник может подтвердить принцип, но не ваше разрешение. NIST AI RMF — добровольная рамка управления рисками; она помогает разложить вопросы по функциям Govern, Map, Measure и Manage, но не заменяет договор и локальную policy. OWASP перечисляет чувствительную информацию и меры снижения риска, но не принимает решение за владельца данных. Поэтому перед реальным запуском нужно повторно закрепить версию документа, plan, region, surface, destination и исключения.</p>\n<p>Локальная модель уменьшает внешний egress, но не отменяет контроль доступа и журналы на вашей машине. Маскирование снижает объём, но не гарантирует анонимизацию. Человеческое approval полезно только вместе с ограниченным scope и сроком; устное «можно AI» нельзя переносить на соседний record.</p>\n<h2>Проверяемый результат</h2>\n<p>Review можно считать завершённым, когда другой инженер повторяет его без доступа к исходному обсуждению. Для restricted-карточки тест должен показать stop до вызова клиента, отсутствие payload в prompt builder, сохранённую причину и назначенного владельца следующего вопроса. Для разрешённой учебной карточки должны совпасть class, условия, destination, requester, issuer и expiry. Это не подтверждает правду о vendor; это подтверждает, что ваша локальная процедура не принимает неизвестное за известное.</p>\n<p>Практический следующий шаг — взять один тип рабочего контекста, но не копировать его в новый документ. Заполните пустую карточку четырьмя блоками: class и owner; policy и contract; маршрут; полномочия и срок. Затем намеренно оставьте один блок пустым и убедитесь, что workflow останавливается без payload. Если причина и следующий владелец не появляются в результате, сначала исправьте review-контур.</p>\n<h2>Проверяемые источники</h2><ul><li><a href=\"https://www.nist.gov/publications/artificial-intelligence-risk-management-framework-generative-artificial-intelligence\" target=\"_blank\" rel=\"noopener\">NIST AI 600-1: Artificial Intelligence Risk Management Framework: Generative Artificial Intelligence Profile</a> — официальный профиль NIST от 26 июля 2024 года; описывает профиль как добровольный companion resource для выявления и управления рисками генеративного ИИ. Он не разрешает передачу конкретного лога конкретному сервису.</li><li><a href=\"https://csrc.nist.gov/pubs/sp/800/207/final\" target=\"_blank\" rel=\"noopener\">NIST SP 800-207: Zero Trust Architecture</a> — официальная публикация NIST о том, что доверие не следует только из сетевого расположения, а authentication и authorization являются раздельными функциями. Она не заменяет egress-конфигурацию и data policy.</li><li><a href=\"https://owasp.org/www-project-top-10-for-large-language-model-applications/assets/PDF/OWASP-Top-10-for-LLMs-v2025.pdf\" target=\"_blank\" rel=\"noopener\">OWASP Top 10 for LLM Applications v2025, LLM02: Sensitive Information Disclosure</a> — официальный материал OWASP о раскрытии PII, финансовых данных, учётных данных и другой чувствительной информации. Он рекомендует санитарную обработку данных и ясные условия использования, но не является договором и не устанавливает правовой режим вашей организации.</li></ul>"
}