anthropic

Токены MCP на Linux хранятся открытым текстом

Promtime

anthropic

Проверка Claude Code 2.1.257 на Linux показала, что access-токены подключённых удалённых MCP-серверов хранятся в ~/.claude/.credentials.json обычным JSON, а единственной защитой служат права доступа 0600. Разбор опубликован в блоге Secretspec, при том что документация Claude Code по MCP описывает хранение токенов как безопасное.

Коротко

  • Файл содержит объект mcpOAuth верхнего уровня, где для каждого сервера сохранены accessToken, clientId, discoveryState, redirectUri, serverName и serverUrl; в разборе приведена запись cloudflare-observability с затёртыми значениями.
  • Поведение совпадает с документацией Anthropic: на macOS используется зашифрованный Keychain с откатом в тот же файл, на Windows защита сводится к правам доступа каталога профиля пользователя.
  • У Codex хранилище OAuth-данных для MCP настраивается через параметр mcp_oauth_credentials_store с режимами auto, file и keyring, поэтому плоский файл на Linux там не единственный доступный вариант.

Файл с bearer-токенами к внешним сервисам это привлекательная цель: любой процесс, работающий от имени пользователя, читает его без дополнительных барьеров, а резервные копии и синхронизация домашнего каталога разносят его дальше. Судя по разбору, проблема не в реализации OAuth, а в том, что стандарт вообще не описывает локальное хранилище, и решение остаётся за клиентом. Настраиваемое хранилище в Codex показывает, что выбор технически реализуем уже сейчас.

Внутри .credentials.json нашёлся объект mcpOAuth с токенами каждого сервера

Автор разбора аутентифицировался в нескольких удалённых MCP-серверах через Claude Code 2.1.257 и затем открыл ~/.claude/.credentials.json. Файл имеет режим 0600, как и должно быть, но содержит объект mcpOAuth верхнего уровня, в котором для каждого сервера лежит выданный access-токен. Права доступа остаются единственным барьером.

В опубликованном примере запись cloudflare-observability включает поля accessToken, clientId, discoveryState, redirectUri, serverName и serverUrl; значения учётных данных в тексте затёрты. Такого набора достаточно, чтобы обращаться к удалённому серверу от имени пользователя без повторного входа в браузере, поскольку токен предъявляется как bearer.

Такое поведение описано в документации Anthropic по управлению учётными данными: на macOS Claude Code кладёт токены в зашифрованный Keychain и откатывается к ~/.claude/.credentials.json, когда Keychain недоступен. На Linux используется тот же файл с режимом 0600, а на Windows это каталог .claude внутри %USERPROFILE%, наследующий права доступа профиля пользователя.

OAuth решает выдачу и обновление токена, но не описывает локальное хранилище

Браузерный вход прячет секрет: пользователь выполняет claude mcp login, подтверждает доступ в браузере и возвращается к подключённому MCP-серверу. Токен никто не создавал вручную, не копировал из панели управления и не вставлял в конфигурацию, но Claude Code его получил и обязан сохранить, чтобы соединение пережило перезапуск.

OAuth здесь решает свои задачи: Claude Code находит сервер авторизации, запрашивает конкретные скоупы, выполняет обмен кода на токен, обновляет access-токен и отзывает выданное разрешение. Документация Anthropic по MCP дополнительно позволяет пользователю зафиксировать набор запрашиваемых скоупов. Спецификация OAuth при этом ничего не говорит о защищённом локальном хранилище.

В сравнении, которое приводит Secretspec, OAuth-учётные данные и API-токен с ограниченной областью действия ведут себя одинаково по ключевым пунктам: клиент обязан хранить оба, у обоих бывают ограниченные права и срок жизни, оба воспроизводимы при краже. Независимый отзыв доступен для API-токена и обычно для OAuth.

SecretSpec 0.20 предлагает 33 провайдера в качестве сменного хранилища

Разбор предлагает вынести персистентность в сменный слой: OAuth-клиент Claude Code обращается к интерфейсу хранилища учётных данных, за которым стоят SecretSpec и выбранный пользователем провайдер. В SecretSpec 0.20 таких интеграций 33: локальные кейринги, менеджеры паролей, зашифрованные файлы, облачные секрет-менеджеры и цели развёртывания.

Точкой интеграции назван Node.js/TypeScript SDK SecretSpec, который встраивает резолвер на Rust, поэтому отдельный код под каждый бэкенд на стороне TypeScript не требуется. Claude Code сериализовал бы одну OAuth-запись на сервер и просил бы SecretSpec её загрузить, сохранить или удалить; провайдеры объявляют свои возможности, так что хранилище вправе требовать чтения и записи.

Провайдер по умолчанию задаётся в пользовательской конфигурации, например командой secretspec config global init --provider keyring --profile default. Организация может потребовать OpenBao или облачный секрет-менеджер, а рабочая станция без графической сессии обойтись хранилищем, зашифрованным age; сам OAuth-поток не меняется, меняется только слой хранения.

Что изменится в SecretSpec 0.21 У SecretSpec уже есть хелперы учётных данных для Git и Docker, а для Nix предложен универсальный резолвер секретов с привязкой к операциям. В работе версионированный резолвер и IPC для провайдеров: приложения смогут обращаться к провайдерам по локальному протоколу, без встраивания SDK и кода бэкендов. Дата выпуска версии не называется.

Хранение MCP-токенов в Claude Code сегодня может изменить только Anthropic: публичный репозиторий проекта не включает реализацию основного CLI, а лицензия заявлена как all rights reserved. Авторы SecretSpec отмечают, что в открытых проектах границу обращения с секретами можно проверить и исправить в апстриме.

Комментарии

Пока никто не написал. Будьте первым.

Присоединяйтесь к разговору

Войдите через Google, чтобы оставить комментарий. Имя и аватар подставятся из вашего профиля Google, а комментарий появится после модерации.

Из Google мы используем только имя и аватар. Почту не сохраняем.