使用操作系統的原生憑證存儲 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(字符串,必需)- 服務或應用程序名稱options.name(字符串,必需)- 用戶名或賬戶標識符
返回值:
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(字符串,必需)- 服務或應用程序名稱options.name(字符串,必需)- 用戶名或賬戶標識符value(字符串,必需)- 要存儲的密碼或憑證
說明:
- 如果已存在給定服務/名稱組合的憑證,它將被替換
- 存儲的值由操作系統加密
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(字符串,必需)- 服務或應用程序名稱options.name(字符串,必需)- 用戶名或賬戶標識符
返回值:
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 憑證管理器中
- 可在控制面板 → 憑證管理器 → Windows 憑證中查看
- 使用
CRED_PERSIST_ENTERPRISE標志持久化,因此按用戶范圍限定 - 使用 Windows 數據保護 API 加密
安全注意事項
- 加密:憑證由操作系統的憑證管理器加密
- 訪問控制:只有存儲憑證的用戶才能檢索它
- 無明文:密碼從不以明文存儲
- 內存安全:Bun 在使用後清零密碼內存
- 進程隔離:憑證按用戶賬戶隔離
限制
- 最大密碼長度因平台而異(通常為 2048-4096 字節)
- 服務和名稱長度應合理(< 256 個字符)
- 某些特殊字符可能需要轉義,具體取決於平台
- 需要適當的系統服務:
- Linux:密鑰服務守護進程必須正在運行
- macOS:必須可用鑰匙串訪問
- Windows:必須啟用憑證管理器服務
與環境變量的比較
與環境變量不同,Bun.secrets:
- ✅ 靜態加密憑證(感謝操作系統)
- ✅ 避免在進程內存轉儲中暴露憑證(內存不再需要時被清零)
- ✅ 在應用程序重啟後存活
- ✅ 可更新而無需重啟應用程序
- ✅ 提供用戶級訪問控制
- ❌ 需要操作系統憑證服務
- ❌ 對部署憑證不太有用(生產環境使用環境變量)
最佳實踐
使用描述性的服務名稱:匹配工具或應用程序名稱 如果你正在構建供外部使用的 CLI,你可能應該使用 UTI(統一類型標識符)作為服務名稱。
javascript// 好 - 匹配實際工具 { service: "com.docker.hub", name: "username" } { service: "com.vercel.cli", name: "team-name" } // 避免 - 太通用 { service: "api", name: "key" }僅用於憑證:不要在此 API 中存儲應用程序配置 此 API 很慢,你可能仍然需要使用配置文件來做某些事情。
用於本地開發工具:
- ✅ 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;
}