{ "index": 99, "slug": "editorial-2025-04-practice-ai-data-privacy", "title": "Перед AI-инструментом данные должны пройти четыре независимые проверки", "excerpt": "Практический маршрут для безопасной передачи контекста: классификация записи, условия сервиса, egress и scoped authorization с воспроизводимым fail-closed тестом.", "contentHtml": "
Инженер открывает AI-чат, чтобы разобраться с ошибкой, и вставляет десять строк production-лога. В строках остаются email, внутренний идентификатор и фрагмент заголовка запроса. Ответ выглядит полезным, но по нему нельзя понять, какие ещё данные добавила выбранная поверхность, какой endpoint получил запрос и имел ли сотрудник право передавать именно эту запись.
\nЗдесь полезно разделить четыре независимых вопроса: что находится во фрагменте, разрешает ли policy выбранную обработку, куда технически уйдёт запрос и кто выдал полномочия на конкретный scope. «Корпоративный» аккаунт, allow-list в firewall или согласие в чате отвечает только на часть вопросов. Если любой из них не доказан, безопасный результат — остановить отправку и оставить причину для следующей проверки.
\nДо открытия внешнего интерфейса зафиксируйте объект передачи. У него должны быть recordId, версия, источник, владелец данных и назначенный класс. Класс описывает содержимое, а не право отправить его наружу: public, internal и restricted — это свойства данных, но не готовое разрешение для любого сервиса.
Минимальная карточка должна связывать данные с решением. В ней укажите цель, requester (инициатора), назначение, версию policy, условие хранения, срок действия и ссылку на полномочие владельца. Не подменяйте неизвестный класс словом «внутренний» и не переносите разрешение с соседнего файла: другой record, режим или endpoint создаёт новое решение.
\nПоле чата показывает только часть контекста. Конкретный продукт может добавить историю диалога, имя репозитория, открытый файл, результаты поиска, выбранные расширения или служебные параметры. Это не универсальное свойство всех AI-инструментов: состав нужно проверять для конкретной поверхности, тарифного режима и включённых функций.
\nДокументация GitHub для Copilot Chat в GitHub прямо описывает обработку пользовательского ввода вместе с контекстом репозитория и, в некоторых случаях, результатами Bing. В той же документации указано, что ответы нужно проверять, а для чувствительного кода — просматривать и тестировать результат. Поэтому в карточке фиксируйте не только текст вопроса, но и surface, включённые источники контекста и фактическое назначение.
\nПосле формирования запроса нужен второй барьер: prompt builder не должен принимать данные, пока детерминированная проверка не вернула разрешённый исход. Это не оценка качества ответа модели. Её результат может быть неверным даже при полностью разрешённом payload, поэтому проверка безопасности и проверка корректности ответа остаются разными этапами.
\nПервая проверка отвечает за содержимое: классификатор или владелец данных подтверждает, что record относится к заявленному классу после маскирования. Вторая отвечает за условия обработки: policy и договор должны относиться к выбранному продукту, режиму, региону и сроку хранения. Слово «анонимизированный» само по себе не заменяет проверку остаточных идентификаторов и возможности восстановления субъекта.
\nТретья проверка отвечает за маршрут. Allow-list говорит, что соединение разрешено к определённому назначению, но не делает любой payload допустимым. Укажите endpoint, прокси, включённый поиск и дополнительные интеграции. Если правило разрешает домен шире, чем договорённый сервис, это повод сузить маршрут или остановить отправку.
\nЧетвёртая проверка отвечает за полномочия. Requester должен иметь доступ к record, а владелец должен разрешить цель, объём, назначение и срок. Авторизация без recordId и expiry не даёт воспроизводимого scope. Принцип zero trust NIST формулирует похожую границу: доверие не следует выводить только из сетевого положения или принадлежности устройства, а аутентификацию и авторизацию нужно выполнять для конкретного ресурса.
| Контроль | Что доказывает | Чего не доказывает | Действие при пробеле |
|---|---|---|---|
| Класс record | Содержимое отнесено к правилу с владельцем и версией | Что внешний сервис вправе его обрабатывать | Остановить и запросить классификацию |
| Policy и условия сервиса | Для выбранной surface описаны обработка, хранение и режим | Что запрос пошёл именно по этому маршруту | Проверить конкретный план, endpoint и дату действия |
| Egress allow-list | Сеть допускает заявленное техническое назначение | Что payload соответствует policy и полномочиям | Не отправлять данные до сверки с классом и scope |
| Scoped authorization | Конкретный requester получил срок и цель для record | Что сервис не добавит другой контекст | Получить новое разрешение и проверить surface |
| Request trace | Виден фактический outbound payload и destination | Что ответ модели точен или безопасен для публикации | Отделить расследование передачи от ревью ответа |
Ниже — самостоятельный пример для Node.js 20 и новее. Он работает только с объектами в памяти, не читает файлы, не вызывает модель и не делает HTTP-запрос. Входы с префиксом synthetic- намеренно учебные. Команда запуска показывает отрицательный путь: restricted-класс блокирует hand-off даже при наличии пользователя и маршрута.
const request = {\n recordId: 'synthetic-log-v1',\n dataClass: 'restricted',\n serviceCondition: 'synthetic-reviewed-retention',\n destination: 'synthetic-ai-boundary-alpha',\n requester: 'synthetic-engineering-read',\n authorization: {\n scope: 'synthetic-log-v1',\n purpose: 'synthetic-debugging',\n expiresAt: '2026-12-31T00:00:00Z'\n }\n};\n\nconst policy = {\n allowedClasses: ['synthetic-public'],\n serviceCondition: 'synthetic-reviewed-retention',\n destination: 'synthetic-ai-boundary-alpha',\n purpose: 'synthetic-debugging'\n};\n\nfunction decide(input, rules, now) {\n const checks = {\n classAllowed: rules.allowedClasses.includes(input.dataClass),\n serviceReviewed: input.serviceCondition === rules.serviceCondition,\n routeAllowed: input.destination === rules.destination,\n scopeMatches: input.authorization.scope === input.recordId,\n purposeMatches: input.authorization.purpose === rules.purpose,\n authorizationActive: input.authorization.expiresAt > now.toISOString()\n };\n\n const allowed = Object.values(checks).every(Boolean);\n return {\n decision: allowed ? 'hand-off' : 'stop',\n reason: allowed ? null : Object.entries(checks).filter(([, ok]) => !ok).map(([name]) => name),\n egressPerformed: false\n };\n}\n\nconst result = decide(request, policy, new Date('2026-08-02T12:00:00Z'));\nconsole.log(JSON.stringify(result, null, 2));\nЗапустите сохранённый фрагмент командой node preflight.mjs. Ожидаемый результат содержит \"decision\": \"stop\", причину classAllowed и \"egressPerformed\": false. Важно, что функция не строит prompt и не передаёт его HTTP-клиенту. В реальном проекте замените учебные справочники на версионированную policy, но сохраните такой же отрицательный контракт.
Положительный результат тоже ограничен. Если заменить класс на synthetic-public, функция проверит только перечисленные поля. Она не доказывает реальное поведение поставщика, полноту сетевого журнала, юридическое основание обработки или отсутствие скрытого контекста в интерфейсе. Это gate перед следующим полномочным шагом, а не сертификат безопасности.
Сначала остановите повторение: отключите интеграцию или запретите дальнейший egress для этой surface. Не удаляйте единственную копию доказательств до согласования с владельцем расследования. Сохраните минимум, необходимый для анализа: время, requester, идентификатор операции, destination, режим продукта и классификацию без копирования чувствительного payload в новый тикет.
\nЗатем разделите два вопроса. Факт передачи устанавливается по журналам клиента, прокси и поставщика, если они доступны. Состав исходного payload устанавливается по журналу формирования запроса, а не по ответу модели. Если одного из журналов нет, это ограничение результата расследования, а не основание считать, что утечки не было.
\nrecordId, версию, источник, владельца и цель. В заявке используйте ссылку или безопасный отпечаток, а не необработанный лог.Этот маршрут не классифицирует данные автоматически и не заменяет DLP, privacy review, договор с поставщиком, управление доступом или требования закона. NIST AI RMF — добровольная рамка для управления рисками AI; она помогает организовать функции govern, map, measure и manage, но не выдаёт разрешение на конкретную запись. Решение о законности обработки остаётся у вашей организации и зависит от юрисдикции и контекста.
\nЛокальная модель уменьшает внешний маршрут, но не отменяет права доступа, локальное хранение журналов и риск попадания секрета в историю. Маскирование уменьшает объём, но может оставить комбинацию полей, по которой субъект восстанавливается. Человеческое согласование полезно для спорных случаев, но оно не должно быть единственным барьером, если запросы проходят через автоматическую интеграцию.
\nПроверка также не говорит, что ответ модели верен. Для кода нужны ревью, тесты и сканирование зависимостей; для инцидента — независимое подтверждение фактов; для решения о клиенте — отдельная процедура. Без этих этапов безопасный hand-off всё равно может привести к неверному действию.
\nПуть можно включать для конкретного типа данных, если другой инженер воспроизведёт его без устного контекста. Должны существовать версия классификации, ссылка на policy для конкретной surface, запись проверенного destination, scoped authorization и журнал решения. Негативный тест должен показывать stop при restricted-классе, чужом scope, истёкшем сроке и неизвестном endpoint.
\nЕсли хотя бы одно доказательство отсутствует, не называйте результат «разрешённым». Оставьте данные локально, верните причину отказа и назначьте владельца следующего вопроса. После появления недостающего evidence повторите весь pre-flight: изменение режима, маршрута или версии record делает старое решение неприменимым.
\n