Git Credential Manager 凭据存储(Credential Stores)完全指南:八大存储后端的选择、配置与底层实现
【免费下载链接】git-credential-managerSecure, cross-platform Git credential storage with authentication to GitHub, Azure Repos, and other popular Git hosting services.项目地址: https://gitcode.com/GitHub_Trending/gi/git-credential-manager
Git Credential Manager(GCM)为 Windows、macOS 与 Linux 提供了多种凭据存储后端(Credential Store),用于安全保存访问 GitHub、Azure Repos、GitLab、Bitbucket 等远程仓库所需的用户名与令牌。本文以 docs/credstores.md 为主干,逐一讲解八大存储后端的适用平台、配置命令、默认路径、限制条件,并结合本仓库源码(如 CredentialStore.cs、PlaintextCredentialStore.cs、GpgPassCredentialStore.cs 等)剖析其底层实现原理。读完本文,你将能够根据操作系统与安全需求,正确选择并配置 GCM 的凭据存储方案,并理解无头(headless)环境下 GPG/Secret Service 等后端的排障要点。
一、凭据存储后端总览
GCM 共支持八种凭据存储选项:
| 后端标识 | 名称 | 可用平台 | 是否默认 |
|---|---|---|---|
wincredman | Windows Credential Manager | Windows | Windows 默认 |
dpapi | DPAPI 保护文件 | Windows | 否 |
keychain | macOS Keychain | macOS | macOS 默认 |
secretservice | freedesktop.org Secret Service API | Linux | 否 |
gpg | GPG /pass兼容文件 | macOS、Linux | 否 |
cache | Git 内置 credential cache | macOS、Linux | 否 |
plaintext | 明文文件 | Windows、macOS、Linux | 否 |
none | 透传/空操作(不存储) | Windows、macOS、Linux | 否 |
默认值规则:macOS 与 Windows 的默认存储分别是 macOS Keychain 与 Windows Credential Manager;而GCM 在 Linux 发行版上没有默认存储后端——这意味着在 Linux 上你必须显式配置GCM_CREDENTIAL_STORE或credential.credentialStore,否则 GCM 无法持久化凭据。
这一默认逻辑在源码中有清晰体现:CredentialStore.cs 的GetDefaultStore()方法:
private static string GetDefaultStore() { if (PlatformUtils.IsWindows()) return StoreNames.WindowsCredentialManager; if (PlatformUtils.IsMacOS()) return StoreNames.MacOSKeychain; // Other platforms have no default store return null; }存储后端的合法标识在 Constants.cs 的CredentialStoreNames中集中定义:wincredman、dpapi、keychain、gpg、secretservice、plaintext、cache、none。
如何选择存储后端
通过设置环境变量GCM_CREDENTIAL_STORE或Git 配置项credential.credentialStore即可选择后端,两者二选一即可(环境变量优先于 Git 配置,见 Settings.cs 中CredentialBackingStore属性对TryGetSetting的调用顺序)。例如:
git config --global credential.credentialStore gpg后端分发与平台校验的底层实现
所有后端统一实现ICredentialStore接口(Get/GetAccounts/AddOrUpdate/Remove),由门面类 CredentialStore.cs 的EnsureBackingStore()根据所选名称懒加载对应实现,并对平台适配性做前置校验:
wincredman:仅限 Windows,且要求当前登录会话允许持久化(WindowsCredentialManager.CanPersist(),见下文)。dpapi:仅限 Windows;默认存储根目录为%USERPROFILE%\.gcm\dpapi_store,可用GCM_DPAPI_STORE_PATH环境变量或credential.dpapiStorePathGit 配置覆盖。keychain:仅限 macOS。secretservice:仅限 Linux,且要求当前为图形会话(IsDesktopSession)。gpg:仅限 POSIX(macOS/Linux);headless 环境下要求设置GPG_TTY或SSH_TTY,否则直接报错。cache:不可用于 Windows(Git for Windows 缺乏 UNIX socket 支持),并读取GCM_CREDENTIAL_CACHE_OPTIONS/credential.cacheOptions作为附加参数。plaintext:全平台可用,无平台限制。none:全平台可用,直接返回NullCredentialStore空操作实现。
若配置了未知的后端名称,GCM 会抛出异常,并在错误信息中列出当前平台所有可用的后端清单(见AppendAvailableStoreList)。
二、Windows Credential Manager(wincredman)
可用平台:Windows默认状态:Windows 上的默认存储后端。⚠️ 限制:在通过网络/SSH 会话连接到 Windows 机器时无法工作。
SET GCM_CREDENTIAL_STORE="wincredman"或
git config --global credential.credentialStore wincredman实现原理
该后端使用 Windows 凭据 API(wincred.h)将数据安全地存入 Windows Credential Manager(早期 Windows 版本中也称为 Windows Credential Vault)。对应实现为 WindowsCredentialManager.cs,它通过 P/Invoke 调用Advapi32中的CredRead、CredWrite、CredEnumerate、CredDelete等原生 API 完成凭据增删改查。
你可以通过控制面板的"凭据管理器",或使用cmdkey命令行工具访问和管理其中的数据。
为什么网络/SSH 会话下不可用
当通过 SSH 等网络会话连接 Windows 机器时,GCM 无法将凭据持久化到 Windows Credential Manager,这是 Windows 系统本身的限制;通过远程桌面(Remote Desktop)连接则不受此限制。源码层面,CredentialStore.cs 在加载该后端前会调用WindowsCredentialManager.CanPersist(),其实现(WindowsCredentialManager.cs)通过CredGetSessionTypes查询当前会话允许的持久化级别:
public static bool CanPersist() { uint count = Advapi32.CRED_TYPE_MAXIMUM; var arr = new CredentialPersist[count]; int result = Win32Error.GetLastError(Advapi32.CredGetSessionTypes(count, arr)); CredentialPersist persist = CredentialPersist.None; if (result == Win32Error.Success) { persist = arr[(int)CredentialType.Generic]; } // If the maximum allowed is anything less than "local machine" then cannot persist credentials. return persist >= CredentialPersist.LocalMachine; }即当会话类型不支持持久化到"本机"级别时,GCM 会判定无法使用该后端并抛出错误。这类场景下,可改用下一节的 DPAPI 文件存储。
三、DPAPI 保护文件(dpapi)
可用平台:Windows
SET GCM_CREDENTIAL_STORE="dpapi"或
git config --global credential.credentialStore dpapi文件结构与加密方式
该后端使用 Windows DPAPI 加密凭据,并将其以文件形式保存在文件系统中。文件结构与后面的明文文件后端一致,唯一区别是第一行(即秘密值)受 DPAPI 保护:写入时先用ProtectedData.Protect(plainBytes, null, DataProtectionScope.CurrentUser)加密再 Base64 编码,读取时反向用ProtectedData.Unprotect解密(见 DpapiCredentialStore.cs)。
一个典型文件内容如下(第一行为 Base64 密文,后续行为元数据):
<base64-encoded-DPAPI-ciphertext> service=https://github.com account=octocatDataProtectionScope.CurrentUser意味着密文只能由加密它的同一个 Windows 用户解密,其他用户即使拿到文件也无法还原明文。
存储路径与目录自动创建
默认文件存放在%USERPROFILE%\.gcm\dpapi_store,可通过环境变量GCM_DPAPI_STORE_PATH(或 Git 配置credential.dpapiStorePath)修改。若目录不存在,GCM 会自动创建(CredentialStore.cs 的ValidateDpapi负责解析路径,目录创建由文件系统操作完成)。相比 wincredman,DPAPI 文件后端不依赖会话持久化能力,可作为网络会话场景的替代方案。
四、macOS Keychain(keychain)
可用平台:macOS默认状态:macOS 上的默认存储后端。
export GCM_CREDENTIAL_STORE=keychain # 或 git config --global credential.credentialStore keychain实现原理
该后端使用 macOS 默认钥匙串(Keychain),通常是login钥匙串。实现位于 MacOSKeychain.cs,通过 P/Invoke 调用 Security Framework(SecurityFramework.cs)中的SecItemAdd、SecItemCopyMatching、SecItemUpdate、SecItemDelete等 API 操作钥匙串条目。
你可以使用"钥匙串访问"(Keychain Access)应用管理其中存储的数据。若在 macOS 上遇到钥匙串权限弹窗,允许 GCM 访问login钥匙串即可正常工作。
五、freedesktop.org Secret Service API(secretservice)
可用平台:Linux⚠️ 限制:需要图形用户界面(GUI)会话。
export GCM_CREDENTIAL_STORE=secretservice # 或 git config --global credential.credentialStore secretservice实现原理
该后端通过libsecret库与系统的 Secret Service 守护进程交互(实现见 SecretServiceCollection.cs 及其 P/Invoke 绑定 Libsecret.cs、Glib.cs、Gobject.cs),将凭据安全地存储在 Secret Service 的"集合"(collections)中。在 GNOME 桌面(gnome-keyring)与 KDE(KWallet)等环境中均有 Secret Service 实现,用户可以使用secret-tool、seahorse等工具查看这些凭据。
GCM 使用的 schema 名为com.microsoft.GitCredentialManager,通过service与account两个字符串属性索引凭据;写入时调用secret_service_store_sync,查询时调用secret_service_search_sync,删除时调用secret_service_clear_sync(并处理 collection 被锁定时的自动解锁流程)。
为什么需要图形会话
当 Secret Service 集合处于锁定状态时,需要弹出图形化的安全提示框请求用户解锁集合。因此若当前是纯 TTY/无桌面会话,GCM 会在加载该后端时直接拒绝——CredentialStore.cs 的ValidateSecretService会检查_context.SessionManager.IsDesktopSession,非桌面会话即抛出 "Cannot use the 'secretservice' credential backing store without a graphical interface present."。这正是 headless 场景下应改用gpg后端的原因。
六、GPG /pass兼容文件(gpg)
可用平台:macOS、Linux⚠️ 前置条件:需要gpg、pass以及一对有效的 GPG 密钥。
export GCM_CREDENTIAL_STORE=gpg # 或 git config --global credential.credentialStore gpg初始化pass存储
该后端使用 GPG 加密包含凭据的文件,文件结构兼容流行的pass工具。使用前必须先用pass工具初始化存储,而初始化又需要有效的 GPG 密钥对:
pass init <gpg-id>其中<gpg-id>是系统上某对 GPG 密钥的用户 ID。若还没有密钥对,先执行:
gpg --gen-key按提示完成创建后再执行pass init。
实现原理与文件布局
实现位于 GpgPassCredentialStore.cs,它继承明文文件后端的目录结构逻辑,但:
- 文件扩展名为
.gpg(而非.credential),内容整体经 GPG 加密; - 加密前,先把密码放在第一行,后续行写入
service=与account=元数据,再整体交给 Gpg.cs 执行gpg --encrypt --batch --recipient "<gpg-id>" --output "<path>";读取时执行gpg --batch --decrypt "<path>"还原明文再解析; - 通过沿目录层级向上查找最近的
.gpg-id文件来确定加密所用的收件人(recipient),这与 GNU Pass 的行为一致(GpgPassCredentialStore.cs)。若在存储根目录也找不到.gpg-id,会抛出提示 "runpass init <gpg-id>to initialize the store"。
默认文件存放在~/.password-store,可通过pass的环境变量PASSWORD_STORE_DIR(或 Git 配置credential.gpgPassStorePath)修改。注意 GCM 会优先使用gpg2(若 PATH 中存在),否则回退到gpg;也可以用GCM_GPG_PATH环境变量显式指定 GPG 可执行文件路径(CredentialStore.cs)。
上述行为均有测试覆盖,例如 GnuPassCredentialStoreTests.cs 验证了凭据文件路径形如namespace/https/example.com/<uuid>/<username>.gpg、.gpg-id向上逐级查找、以及不同子目录各自持有独立.gpg-id时按最近者加密的行为。
Headless / 纯 TTY 会话
在无图形界面的 headless/TTY 环境中使用gpg后端,必须为 GPG Agent(gpg-agent)配置合适的终端 pin-entry 程序,例如pinentry-tty或pinentry-curses。
- 若通过 SSH 连接系统,
SSH_TTY变量通常会被自动设置。GCM 会把SSH_TTY的值作为 TTY 设备传给 GPG/GPG Agent 用于输入口令(见 Gpg.cs 的PrepareEnvironment:headless 会话下若未显式设置GPG_TTY而存在SSH_TTY,则用SSH_TTY填充子进程的GPG_TTY)。 - 若并非通过 SSH 连接,或未设置
SSH_TTY,则必须在运行 GCM 之前设置GPG_TTY环境变量。最简单的方法是在 profile(~/.bashrc、~/.profile等)中加入:
export GPG_TTY=$(tty)注意:这里不能使用/dev/tty,必须使用tty命令返回的真实 TTY 设备路径。另外,若在 headless 会话下GPG_TTY与SSH_TTY均未设置,GCM 会在加载该后端时直接报错(提示添加export GPG_TTY=$(tty)到 profile),见 CredentialStore.cs。
七、Git 内置的凭据缓存(cache)
可用平台:macOS、Linux(不可用于 Windows)
export GCM_CREDENTIAL_STORE=cache # 或 git config --global credential.credentialStore cache适用场景
该后端使用 Git 自带的易失性内存凭据缓存(git credential-cache)。它可以帮助你减少重复认证的次数,但不要求把凭据写入持久化存储,非常适合 Azure Cloud Shell 或 AWS CloudShell 这类场景——既不想在磁盘上留下凭据,又不希望在每次 Git 操作时重新认证。
缓存时长与自定义选项
默认情况下git credential-cache会将凭据缓存900 秒(15 分钟)。该时长以及其他 git-credential-cache 支持的全部选项,可以通过环境变量GCM_CREDENTIAL_CACHE_OPTIONS或 Git 配置credential.cacheOptions修改(例如--timeout;文档同时提示--socket选项虽未经测试与官方支持,但理论上没有不工作的理由):
export GCM_CREDENTIAL_CACHE_OPTIONS="--timeout 300" # 或 git config --global credential.cacheOptions "--timeout 300"实现原理
实现位于 CredentialCacheStore.cs,它本质上是把操作转发给 Git 自身:通过git.InvokeHelperAsync依次调用git credential-cache store、git credential-cache get、git credential-cache erase,并把配置好的_options追加到命令尾部。由于依赖 Git for Windows 的 UNIX socket 支持,该后端在 Windows 上不可用(CredentialStore.cs)。此外,缓存后端不支持枚举账号列表,GetAccounts只能尽力返回首个凭据的用户名或空列表。
八、明文文件(plaintext)
可用平台:Windows、macOS、Linux⚠️ 警告:这不是一种安全的凭据存储方式!
export GCM_CREDENTIAL_STORE=plaintext # 或 git config --global credential.credentialStore plaintext文件格式与默认路径
该后端将凭据以明文文件形式保存在文件系统中。默认存放于~/.gcm/store(macOS/Linux)或%USERPROFILE%\.gcm\store(Windows),可通过环境变量GCM_PLAINTEXT_STORE_PATH(或 Git 配置credential.plaintextStorePath)修改。目录不存在时会自动创建。
文件采用"密码首行 + 元数据行"的格式,一个账户对应一个以.credential结尾的文件(见 PlaintextCredentialStore.cs 的SerializeCredential):
my-secret-password service=https://github.com account=octocat服务名会被转换为目录层级(形如https/github.com/<路径>),账户名作为文件名;同名同值写入会被跳过,避免无谓的磁盘 I/O。
POSIX 权限处理
在 POSIX 平台上,新建的存储目录会被设置为仅属主可读写执行(700或drwx------),已有目录的权限则不会被修改(PlaintextCredentialStore.cs 的EnsureStoreRoot,通过chmod设置S_IRUSR | S_IWUSR | S_IXUSR)。
与 git-credential-store 的区别
GCM 的明文存储与 Git 自带的 git-credential-store 是两个不同的实现,尽管文件格式相似,默认路径也不同(GCM 用~/.gcm/store,git-credential-store 默认用~/.git-credentials)。
⚠️ 严重安全警告
此存储机制不安全!秘密与凭据以明文文件保存,不带任何安全保护!
强烈建议始终使用上述其他存储后端之一。该选项仅用于兼容性,以及在没有其他安全方案可用的环境中。
如果确实要使用该后端,强烈建议:把目录权限设置为禁止其他用户或应用访问;如果可能,将路径放在随身携带的外部卷上,并使用全盘加密。
九、Passthrough / 空操作(none)
可用平台:Windows、macOS、Linux
SET GCM_CREDENTIAL_STORE="none"或
git config --global credential.credentialStore none用途与注意事项
该选项禁用 GCM 内部凭据存储:所有存储或检索凭据的操作都不做任何事并直接返回成功(对应实现为 NullCredentialStore.cs)。典型用途是:你希望使用另一个凭据存储,通过 Git 配置按顺序链式组合多个 credential helper,而不想让 GCM 自己存凭据。
注意:使用该选项时,务必确保另一个 credential helper 在 Git 配置credential.helper中排在 GCM之前,否则每次与远程仓库交互时你都会被提示输入凭据(因为 GCM 既不存储也不返回凭据,前面的 helper 又拿不到结果时,Git 只能回退到交互式提示)。
十、相关文档与源码指引
- 配置项参考:configuration.md(含
credential.credentialStore等 Git 配置键说明) - 环境变量参考:environment.md(含
GCM_CREDENTIAL_STORE、GCM_DPAPI_STORE_PATH、GCM_PLAINTEXT_STORE_PATH、GCM_CREDENTIAL_CACHE_OPTIONS、GCM_GPG_PATH等) - 后端分发与默认值:src/shared/Core/CredentialStore.cs
- 存储名与环境变量常量:src/shared/Core/Constants.cs
- 明文文件实现:src/shared/Core/PlaintextCredentialStore.cs
- DPAPI 实现:src/shared/Core/Interop/Windows/DpapiCredentialStore.cs
- Secret Service 实现:src/shared/Core/Interop/Linux/SecretServiceCollection.cs
- GPG/pass 实现:src/shared/Core/Interop/Posix/GpgPassCredentialStore.cs、src/shared/Core/Gpg.cs
- 缓存实现:src/shared/Core/CredentialCacheStore.cs
- 相关测试:GnuPassCredentialStoreTests.cs、DpapiCredentialStoreTests.cs
选择建议速查:日常桌面环境 Windows 用默认的wincredman、macOS 用默认的keychain、Linux 桌面用secretservice;SSH 远程会话连 Windows 用dpapi;无图形界面的 Linux/macOS 服务器用gpg(记得配好GPG_TTY);云 Shell 等临时环境用cache;除非万不得已,永远不要用plaintext。
【免费下载链接】git-credential-managerSecure, cross-platform Git credential storage with authentication to GitHub, Azure Repos, and other popular Git hosting services.项目地址: https://gitcode.com/GitHub_Trending/gi/git-credential-manager
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考