8 lines
25 KiB
JSON
8 lines
25 KiB
JSON
{
|
||
"index": 98,
|
||
"slug": "editorial-2025-04-mechanism-ai-data-privacy",
|
||
"title": "Данные и приватность в AI-инструментах: как принять инженерное решение",
|
||
"excerpt": "Передача контекста в AI — это не одно разрешение, а проверяемая цепочка из класса данных, условий обработки, маршрута и полномочий. Разбираем fail-closed контроль с воспроизводимым отрицательным примером.",
|
||
"contentHtml": "<p>Симптом появляется в момент, когда инженер хочет ускорить разбор ошибки: он копирует в AI-инструмент фрагмент лога, тикет или stack trace, а потом не может точно восстановить, что стало контекстом запроса. Корпоративный тариф, разрешённый домен и устное «можно» отвечают на разные вопросы. Ни один из этих фактов сам по себе не доказывает допустимость передачи.</p>\n<p>Цена ошибки измеряется не только возможной утечкой. В журнале могут остаться идентификатор клиента, редкое сочетание событий, токен, внутреннее имя сервиса или сведения, по которым запись легко сопоставить с человеком. После отправки нужно ещё установить, какой провайдер получил запрос, какие дополнительные данные добавила поверхность и какое решение владельца это разрешало. Поэтому решение принимаем до hand-off (передачи контекста), а при неполной проверке останавливаемся.</p>\n<h2>Один запрос — четыре независимых вопроса</h2>\n<p>Удобно разложить передачу на четыре слоя. Первый слой — класс данных: что находится в записи и по какому правилу она получила метку <code>public</code>, <code>internal</code> или <code>restricted</code>. Класс принадлежит конкретной записи, а не папке и не ощущению автора. Маскирование имени не доказывает обезличивание: время, сумма, редкий идентификатор и последовательность событий могут сохранить связь с субъектом.</p>\n<p>Второй слой — допустимость обработки. Здесь проверяют политику компании, договор с поставщиком и фактическую функцию: план, регион, режим, хранение, обучение моделей, subprocessors (привлечённых обработчиков) и журналирование, если они относятся к выбранной поверхности. Слово «корпоративный» — только название тарифа. Оно не заменяет чтение применимых условий.</p>\n<p>Третий слой — egress, то есть технический выход запроса. Он включает destination (конечный адрес), прокси, API-шлюз, DNS и сетевые правила. Разрешённый firewall-маршрут доказывает лишь возможность соединения. NIST прямо отделяет защиту ресурса и проверку идентичности от доверия к сетевому расположению, поэтому доступный домен не превращается в разрешение на данные.</p>\n<p>Четвёртый слой — полномочия. Нужно связать решение с record id, requester (инициатором), ролью владельца, destination, целью и сроком действия. Старое одобрение «для логов» может относиться к другой записи, другой функции или уже истечь. Без этой связи нельзя доказать, что именно данный человек имел право отправить именно этот контекст именно в этот маршрут.</p>\n<figure><img src=\"/assets/editorial/2025/ai-data-privacy-2025-egress-review.svg\" alt=\"Схема проверки AI-контекста: record и class, затем policy и договор, egress destination, полномочия владельца и два исхода — разрешённая передача или безопасная остановка\" loading=\"lazy\" /><figcaption>Слои проверяют разные свойства одной операции. Любое расхождение переводит запрос в безопасную остановку без внешней отправки.</figcaption></figure>\n<h2>Как связать доказательства в один контракт</h2>\n<p>Рассмотрим учебный запрос на объяснение ошибки в платёжном сервисе. В исходной записи есть время события, тип операции, идентификатор клиента и текст исключения. Инженер заменил идентификатор на <code>client-17</code>, но это лишь преобразование значения. Решение о классе должно учитывать весь остаточный набор полей и правило классификации.</p>\n<p>Для проверки заведём карточку операции. Она не обязана содержать сам лог: достаточно ссылки на запись и метаданных, по которым другой инженер сможет повторить решение. Минимальный состав такой: <code>record.id</code>, версия классификации, класс, владелец, версия policy, выбранная функция, destination, сетевой control, requester, цель и <code>expiresAt</code>. Хэш или внутренний идентификатор payload полезен для связи событий, но не должен превращаться в новый журнал с исходными персональными данными.</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>Что именно описывает запись и какие ограничения к ней применимы?</td><td>Удалённое имя или уверенность автора, что фрагмент «безопасный»</td></tr><tr><td>Policy и договор</td><td>Версия условий для плана, функции и региона</td><td>Допускает ли выбранная поверхность такую обработку?</td><td>Название тарифа или общая страница о продукте без нужного scope</td></tr><tr><td>Egress</td><td>Destination, маршрут и сетевой control</td><td>Куда уйдёт запрос и какие компоненты добавят данные?</td><td>Открытый домен, зелёный firewall или локальный прокси без трассировки</td></tr><tr><td>Полномочия</td><td>Record id, requester, owner decision и expiry</td><td>Кто и до какого момента разрешил эту операцию?</td><td>Устное согласие, общий доступ к проекту или старый approval</td></tr></tbody></table></div>\n<p>Важно проверять не четыре флага по отдельности, а их пересечение. Разрешённый класс и подходящий договор не помогают, если запрос ушёл через BYOK-провайдера, которого не покрывает договор. Правильный маршрут и действующий доступ также не спасают restricted-запись, если правило классификации запрещает внешнюю обработку. В журнале решения полезно сохранить результат каждой проверки и причину отказа, но не копировать исходный payload.</p>\n<h2>Почему поверхность AI расширяет маршрут данных</h2>\n<p>У пользователя перед глазами может быть одно поле prompt, а система соберёт больше. В официальном описании GitHub Copilot Chat указано, что prompt объединяется с дополнительным контекстом: открытыми файлами, репозиторием и историей чата; на некоторых поверхностях может добавляться веб-поиск. Значит, перед проверкой надо назвать не только текст, который человек собирается вставить, но и функцию, режим, открытые вкладки, индексированные источники и внешние инструменты.</p>\n<p>Это меняет порядок диагностики. Если инженер отправил stack trace из закрытого репозитория, нужно проверить не только строку с ошибкой, но и автоматически выбранные файлы. Если включён веб-поиск, отдельным destination становится поисковый API. Если используется BYOK, GitHub предупреждает, что prompt и ответы передаются выбранному провайдеру и могут подпадать под его правила хранения и приватности. На практике это означает: маршрут нельзя вывести из интерфейса; его нужно подтвердить документацией и конфигурацией конкретной поверхности.</p>\n<p>Контентные исключения и индексирование репозитория тоже имеют узкую роль. Настройка, запрещающая инструменту читать определённые файлы, уменьшает доступный контекст. Она не классифицирует остальные записи, не выдаёт пользователю право на отправку и не отменяет договорные ограничения. Так один control снижает конкретный путь утечки, но не закрывает четыре слоя целиком.</p>\n<h2>Воспроизводимый отрицательный пример</h2>\n<p>Ниже — самостоятельный фрагмент JavaScript для Node.js без сети, файлов и вызова модели. Он проверяет контракт на фиксированных строках. В примере <code>restricted</code> не входит в список классов, разрешённых выбранной policy, поэтому результат должен быть отрицательным. Сохраните код в <code>handoff-check.mjs</code> и выполните <code>node handoff-check.mjs</code>.</p>\n<pre><code>const NOW = new Date('2025-04-15T10:00:00Z');\n\nfunction assess(request) {\n const checks = [\n {\n name: 'class-allowed',\n ok: request.service.allowedClasses.includes(request.record.class),\n },\n {\n name: 'policy-present',\n ok: Boolean(request.service.policyVersion),\n },\n {\n name: 'route-allowed',\n ok: request.route.allowlisted === true\n && request.route.destination === request.authorization.destination,\n },\n {\n name: 'authorization-current',\n ok: request.authorization.recordId === request.record.id\n && new Date(request.authorization.expiresAt) > NOW,\n },\n ];\n\n const failed = checks.filter((check) => !check.ok).map((check) => check.name);\n return {\n accepted: failed.length === 0,\n failed,\n nextAction: failed.length === 0 ? 'human-review' : 'ask-data-owner',\n payloadReleased: false,\n };\n}\n\nconst request = {\n record: { id: 'log-42', class: 'restricted' },\n service: { policyVersion: 'policy-2025-03', allowedClasses: ['public', 'internal'] },\n route: { destination: 'ai.example.test', allowlisted: true },\n authorization: {\n recordId: 'log-42',\n destination: 'ai.example.test',\n expiresAt: '2025-04-30T23:59:59Z',\n },\n};\n\nconsole.log(assess(request));\n// { accepted: false,\n// failed: [ 'class-allowed' ],\n// nextAction: 'ask-data-owner',\n// payloadReleased: false }\n</code></pre>\n<p>Отрицательный результат здесь проверяем: запись не передаётся, а причина указывает на слой классификации. Домен <code>ai.example.test</code> зарезервирован для примеров и не вызывает соединение. Чтобы получить положительный результат, недостаточно удалить поле <code>failed</code> из вывода: нужно изменить класс на разрешённый и заново проверить все четыре условия. В рабочем коде добавьте строгую валидацию схемы, запрет неизвестных полей и отдельный тест для каждой причины отказа.</p>\n<h2>Порядок проверки перед hand-off</h2>\n<ol><li><strong>Зафиксируйте границу.</strong> Назовите record id, выбранную AI-поверхность и цель. Не вставляйте исходный payload в чат для предварительной проверки.</li><li><strong>Опишите данные.</strong> Укажите состав записи, класс, версию правила, владельца и результат маскирования. Если классификация неизвестна, остановите операцию.</li><li><strong>Проверьте условия обработки.</strong> Сопоставьте план, функцию, регион, хранение, обучение, subprocessors и срок действия договора. Если документ не покрывает нужный режим, не переносите вывод из похожего режима.</li><li><strong>Постройте карту egress.</strong> Запишите endpoint, прокси, API-шлюз, DNS, веб-поиск, BYOK и инструменты, которые могут добавить контекст. Для каждого выхода нужен отдельный разрешённый scope.</li><li><strong>Сверьте полномочия.</strong> Проверьте доступ requester к записи, решение владельца, record id, destination, цель и expiry. Общее право на репозиторий не равно праву отправлять его содержимое наружу.</li><li><strong>Сделайте safe stop или hand-off.</strong> При любом пробеле верните <code>accepted: false</code>, стабильную причину, ссылку на evidence и следующий вопрос владельцу. При полном совпадении передайте минимальный разрешённый контекст и сохраните событие без исходного текста.</li></ol>\n<h2>Что журналировать и как проверять контроль</h2>\n<p>Журнал контроля должен помогать восстановить решение, но не становиться вторым каналом утечки. Для каждого запроса можно записать время, идентификатор операции, хэш или ссылку на запись, версии policy и классификации, выбранную поверхность, destination, результат четырёх проверок и причину остановки. Секреты, полный prompt и ответы модели в такой журнал не попадают. Срок хранения самого журнала выбирается по внутренней политике и договору, а не копируется из этого примера.</p>\n<p>Проверка начинается с отрицательных случаев. Подайте тестовую запись класса <code>restricted</code>, просроченное разрешение, другой destination и неизвестную policy. У каждого запуска ожидайте <code>accepted: false</code>; сетевой мониторинг должен показать ноль внешних запросов. Затем отдельно проверьте разрешённый учебный класс и убедитесь, что передача доступна только при совпадении record id, маршрута и срока. Такой тест проверяет поведение шлюза, но не доказывает свойства поставщика после получения запроса.</p>\n<p>Для периодического контроля сравнивайте фактический маршрут с карточкой операции: destination из журнала приложения, адрес прокси и включённые функции должны образовывать одну версию конфигурации. Расхождение — это отказ контроля, а не повод автоматически расширить allowlist. Сначала изолируйте операцию и назначьте владельца исправления, затем повторите проверку.</p>\n<h2>Ограничения применимости</h2>\n<p>Четыре слоя — инженерная модель принятия решения, а не универсальная сертификация. Она не заменяет DLP, инвентаризацию данных, контроль доступа, юридическую оценку, аудит поставщика или требования конкретной юрисдикции. NIST описывает рамки управления риском и zero trust, но не классифицирует ваш лог и не отвечает за условия вашего договора.</p>\n<p>Официальная документация продукта может измениться, а одна и та же функция может работать по-разному в GitHub.com, IDE, CLI и корпоративной конфигурации. Проверяйте дату, версию и surface, на которой действительно работает запрос. Даже доказанный маршрут не показывает, как модель интерпретирует контекст, и не гарантирует корректность ответа. Для критичного кода нужны human review, тесты и независимая проверка результата.</p>\n<p>Маскирование снижает объём раскрываемых данных, но не делает любую запись публичной. Локальная модель уменьшает внешний egress, однако оставляет риски доступа, хранения, журналирования и компрометации хоста. Поэтому статья не отвечает «можно ли всегда отправлять логи». Она задаёт более узкий проверяемый критерий: можно ли доказать допустимость этой записи, этой функции, этого маршрута и этого полномочия в момент операции.</p>\n<h2>Критерий готовности решения</h2>\n<p>Решение готово к ограниченному применению, если другой инженер без исходного payload может взять карточку и воспроизвести четыре ответа: какой класс, какая policy и договорная версия, какой egress, кто разрешил и до какого срока. Для каждого отрицательного case известны код причины, evidence и следующий владелец вопроса. Ни один такой case не запускает внешний запрос. Если хотя бы один ответ строится на слове «обычно», интерфейсе без трассировки или старом согласовании, готов не hand-off, а новая проверка.</p>\n<h2>Проверяемые источники</h2>\n<ul><li><a href=\"https://docs.github.com/en/copilot/responsible-use/chat\" target=\"_blank\" rel=\"noopener noreferrer\">GitHub Docs: Application card — GitHub Copilot Chat</a> — официально описывает состав контекста, возможный веб-поиск, ограничения, ответственность пользователя и особенности BYOK; это не классификация вашего payload и не разрешение на его передачу.</li><li><a href=\"https://docs.github.com/en/copilot/concepts/context\" target=\"_blank\" rel=\"noopener noreferrer\">GitHub Docs: Concepts for providing context to GitHub Copilot</a> — официальная навигация по индексированию репозиториев и content exclusion; настройка ограничения контекста не заменяет проверку полномочий и договора.</li><li><a href=\"https://csrc.nist.gov/pubs/sp/800/207/final\" target=\"_blank\" rel=\"noopener noreferrer\">NIST SP 800-207: Zero Trust Architecture</a> — описывает отсутствие неявного доверия по сетевому расположению и раздельные authentication/authorization; документ не является юридическим заключением.</li><li><a href=\"https://csrc.nist.gov/pubs/sp/800/207/a/final\" target=\"_blank\" rel=\"noopener noreferrer\">NIST SP 800-207A: A Zero Trust Architecture Model for Access Control in Cloud-Native Applications</a> — связывает политики идентичности и сети с API gateway, proxy и гранулярным контролем; это архитектурное руководство, а не готовая конфигурация конкретного проекта.</li><li><a href=\"https://www.nist.gov/publications/artificial-intelligence-risk-management-framework-generative-artificial-intelligence\" target=\"_blank\" rel=\"noopener noreferrer\">NIST AI 600-1: Generative Artificial Intelligence Profile</a> — официальный профиль управления рисками генеративного AI, включая риски утечки, несанкционированного использования и раскрытия данных; он не заменяет отраслевые и договорные требования.</li></ul>"
|
||
}
|