Skip to content

Безопасное хранение и получение конфиденциальных учетных данных с использованием собственных API хранилища учетных данных операционной системы.

typescript
import { secrets } from "bun";

let githubToken: string | null = await secrets.get({
  service: "my-cli-tool",
  name: "github-token",
});

if (!githubToken) {
  githubToken = prompt("Пожалуйста, введите ваш токен GitHub");

  await secrets.set({
    service: "my-cli-tool",
    name: "github-token",
    value: githubToken,
  });

  console.log("Токен GitHub сохранен");
}

const response = await fetch("https://api.github.com/user", {
  headers: { Authorization: `token ${githubToken}` },
});

console.log(`Выполнен вход как ${(await response.json()).login}`);

Обзор

Bun.secrets предоставляет кроссплатформенный API для управления конфиденциальными учетными данными, которые CLI-инструменты и приложения для разработки обычно хранят в виде простого текста в файлах, таких как ~/.npmrc, ~/.aws/credentials или .env файлы. Он использует:

  • macOS: Keychain Services
  • Linux: libsecret (GNOME Keyring, KWallet и т.д.)
  • Windows: Windows Credential Manager

Все операции асинхронны и не блокируют выполнение, работая в пуле потоков Bun.

NOTE

В будущем мы можем добавить дополнительную опцию `provider` для улучшения работы с секретами развертывания в продакшене, но сегодня этот API в основном полезен для локальных инструментов разработки.

API

Bun.secrets.get(options)

Получение сохраненных учетных данных.

typescript
import { secrets } from "bun";

const password = await Bun.secrets.get({
  service: "my-app",
  name: "alice@example.com",
});
// Возвращает: string | null

// Или, если вы предпочитаете без объекта
const password = await Bun.secrets.get("my-app", "alice@example.com");

Параметры:

  • options.service (string, обязательно) - Имя сервиса или приложения
  • options.name (string, обязательно) - Имя пользователя или идентификатор учетной записи

Возвращает:

  • Promise<string | null> - Сохраненный пароль или null, если не найден

Bun.secrets.set(options, value)

Сохранение или обновление учетных данных.

typescript
import { secrets } from "bun";

await secrets.set({
  service: "my-app",
  name: "alice@example.com",
  value: "super-secret-password",
});

Параметры:

  • options.service (string, обязательно) - Имя сервиса или приложения
  • options.name (string, обязательно) - Имя пользователя или идентификатор учетной записи
  • value (string, обязательно) - Пароль или секрет для хранения

Примечания:

  • Если учетные данные уже существуют для данной комбинации service/name, они будут заменены
  • Сохраненное значение шифруется операционной системой

Bun.secrets.delete(options)

Удаление сохраненных учетных данных.

typescript
const deleted = await Bun.secrets.delete({
  service: "my-app",
  name: "alice@example.com",
  value: "super-secret-password",
});
// Возвращает: boolean

Параметры:

  • options.service (string, обязательно) - Имя сервиса или приложения
  • options.name (string, обязательно) - Имя пользователя или идентификатор учетной записи

Возвращает:

  • Promise<boolean> - true, если учетные данные были удалены, false, если не найдены

Примеры

Хранение учетных данных CLI-инструментов

javascript
// Сохранение токена GitHub CLI (вместо ~/.config/gh/hosts.yml)
await Bun.secrets.set({
  service: "my-app.com",
  name: "github-token",
  value: "ghp_xxxxxxxxxxxxxxxxxxxx",
});

// Или, если вы предпочитаете без объекта
await Bun.secrets.set("my-app.com", "github-token", "ghp_xxxxxxxxxxxxxxxxxxxx");

// Сохранение токена реестра npm (вместо ~/.npmrc)
await Bun.secrets.set({
  service: "npm-registry",
  name: "https://registry.npmjs.org",
  value: "npm_xxxxxxxxxxxxxxxxxxxx",
});

// Получение для API-вызовов
const token = await Bun.secrets.get({
  service: "gh-cli",
  name: "github.com",
});

if (token) {
  const response = await fetch("https://api.github.com/name", {
    headers: {
      Authorization: `token ${token}`,
    },
  });
}

Миграция с файлов конфигурации в виде простого текста

javascript
// Вместо хранения в ~/.aws/credentials
await Bun.secrets.set({
  service: "aws-cli",
  name: "AWS_SECRET_ACCESS_KEY",
  value: process.env.AWS_SECRET_ACCESS_KEY,
});

// Вместо .env файлов с конфиденциальными данными
await Bun.secrets.set({
  service: "my-app",
  name: "api-key",
  value: "sk_live_xxxxxxxxxxxxxxxxxxxx",
});

// Загрузка во время выполнения
const apiKey =
  (await Bun.secrets.get({
    service: "my-app",
    name: "api-key",
  })) || process.env.API_KEY; // Резервный вариант для CI/продакшена

Обработка ошибок

javascript
try {
  await Bun.secrets.set({
    service: "my-app",
    name: "alice",
    value: "password123",
  });
} catch (error) {
  console.error("Не удалось сохранить учетные данные:", error.message);
}

// Проверка существования учетных данных
const password = await Bun.secrets.get({
  service: "my-app",
  name: "alice",
});

if (password === null) {
  console.log("Учетные данные не найдены");
}

Обновление учетных данных

javascript
// Начальный пароль
await Bun.secrets.set({
  service: "email-server",
  name: "admin@example.com",
  value: "old-password",
});

// Обновление на новый пароль
await Bun.secrets.set({
  service: "email-server",
  name: "admin@example.com",
  value: "new-password",
});

// Старый пароль заменен

Поведение на платформах

macOS (Keychain)

  • Учетные данные хранятся в связке ключей входа пользователя
  • При первом использовании может появиться запрос на разрешение доступа
  • Учетные данные сохраняются после перезапуска системы
  • Доступны пользователю, который их сохранил

Linux (libsecret)

  • Требуется демон службы секретов (GNOME Keyring, KWallet и т.д.)
  • Учетные данные хранятся в коллекции по умолчанию
  • Может появиться запрос на разблокировку, если связка ключей заблокирована
  • Служба секретов должна быть запущена

Windows (Credential Manager)

  • Учетные данные хранятся в Windows Credential Manager
  • Видны в Панель управления → Credential Manager → Windows Credentials
  • Сохраняются с флагом CRED_PERSIST_ENTERPRISE, поэтому ограничены пользователем
  • Шифруются с использованием Windows Data Protection API

Соображения безопасности

  1. Шифрование: Учетные данные шифруются менеджером учетных данных операционной системы
  2. Контроль доступа: Только пользователь, сохранивший учетные данные, может получить их
  3. Без простого текста: Пароли никогда не хранятся в виде простого текста
  4. Безопасность памяти: Bun очищает память пароля после использования
  5. Изоляция процесса: Учетные данные изолированы по учетной записи пользователя

Ограничения

  • Максимальная длина пароля варьируется в зависимости от платформы (обычно 2048-4096 байт)
  • Имена service и name должны быть разумной длины (< 256 символов)
  • Некоторые специальные символы могут требовать экранирования в зависимости от платформы
  • Требуются соответствующие системные службы:
    • Linux: Демон службы секретов должен быть запущен
    • macOS: Keychain Access должен быть доступен
    • Windows: Служба Credential Manager должна быть включена

Сравнение с переменными окружения

В отличие от переменных окружения, Bun.secrets:

  • ✅ Шифрует учетные данные на диске (благодаря операционной системе)
  • ✅ Избегает раскрытия секретов в дампах памяти процесса (память очищается, когда она больше не нужна)
  • ✅ Сохраняется после перезапуска приложения
  • ✅ Может быть обновлен без перезапуска приложения
  • ✅ Предоставляет контроль доступа на уровне пользователя
  • ❌ Требует службы учетных данных ОС
  • ❌ Не очень полезен для секретов развертывания (используйте переменные окружения в продакшене)

Лучшие практики

  1. Используйте описательные имена сервисов: Соответствуйте имени инструмента или приложения Если вы создаете CLI для внешнего использования, вероятно, следует использовать UTI (Uniform Type Identifier) для имени сервиса.

    javascript
    // Хорошо - соответствует фактическому инструменту
    { service: "com.docker.hub", name: "username" }
    { service: "com.vercel.cli", name: "team-name" }
    
    // Избегайте - слишком общо
    { service: "api", name: "key" }
  2. Только учетные данные: Не храните конфигурацию приложения в этом API Этот API медленный, вам, вероятно, все еще нужно использовать файл конфигурации для некоторых вещей.

  3. Используйте для локальных инструментов разработки:

    • ✅ CLI-инструменты (gh, npm, docker, kubectl)
    • ✅ Локальные серверы разработки
    • ✅ Личные API-ключи для тестирования
    • ❌ Продакшен-серверы (используйте правильное управление секретами)

TypeScript

typescript
namespace Bun {
  interface SecretsOptions {
    service: string;
    name: string;
  }

  interface Secrets {
    get(options: SecretsOptions): Promise<string | null>;
    set(options: SecretsOptions, value: string): Promise<void>;
    delete(options: SecretsOptions): Promise<boolean>;
  }

  const secrets: Secrets;
}

Bun от www.bunjs.com.cn