跳转到内容

凭据管理:钥匙串、SSH 密钥与密码 ​

脚本要连服务器,Git 要推代码,备份要解密仓库。这些都需要凭据,而把凭据写进脚本或配置文件是最省事也最危险的做法。

macOS 自带的钥匙串能解决其中大部分。这一篇讲怎么让凭据既取得到、又不以明文躺在磁盘上。

开始之前 ​

需要能在终端执行命令。本文会生成一对 SSH 密钥并写入钥匙串,两者都可以删除。

先建立一条底线:任何会进版本库的文件里都不能有密码、私钥或令牌。一旦提交过,改密码是唯一的补救——从历史里删掉不算数,内容可能已经被抓取。

钥匙串是什么 ​

钥匙串是 macOS 的凭据存储,内容加密,由你的登录密码保护。Wi-Fi 密码、网站密码、访达保存的共享凭据、证书都在里面。

图形界面是「钥匙串访问」(应用程序 → 实用工具)。macOS 较新版本还提供了「密码」应用,面向网站与应用账号;底层的钥匙串仍然存在,供开发相关的凭据使用。

命令行读写 ​

存一条:

sh
security add-generic-password -a "$USER" -s "我的服务" -w

security 是钥匙串的命令行工具。add-generic-password 添加一条通用密码,-a 指定账号($USER 是当前用户名的环境变量),-s 指定服务名(自己起,用来检索),-w 不跟值时会提示你输入——这样密码不会出现在 shell 历史里。

读出来:

sh
security find-generic-password -s "我的服务" -w

-w 表示只输出密码本身,方便在脚本里使用。首次访问时系统会弹窗要求确认,可以选择「始终允许」。

删掉:

sh
security delete-generic-password -s "我的服务"

在脚本里用 ​

sh
export DB_PASSWORD="$(security find-generic-password -s "我的服务" -w)"

$(...) 把命令的输出作为值。这样脚本文件里只有服务名,没有密码。

比起写在 .env 文件里,好处是:文件误传、误提交、被备份到别处时,泄露的只是一个服务名。

SSH 密钥 ​

生成 ​

sh
ssh-keygen -t ed25519 -C "你的标识"

ssh-keygen 生成密钥对,-t ed25519 指定算法(现在的推荐选择,比 RSA 更短更快),-C 加一段注释用于辨认,通常写邮箱或「机器名-用途」。

会提示设置口令(passphrase),请设置一个。 不设的话,私钥文件本身就是完整的凭据——文件被复制走,对方立刻拥有你所有服务器的访问权。设了口令,拿到文件的人还需要口令。

生成的两个文件:~/.ssh/id_ed25519 是私钥,永远不要发给任何人、不要提交、不要备份到不加密的地方;~/.ssh/id_ed25519.pub 是公钥,可以自由分发,放到服务器上。

让口令不用反复输 ​

macOS 的 ssh-add 可以把口令存进钥匙串:

sh
ssh-add --apple-use-keychain ~/.ssh/id_ed25519

按 man ssh-add 的说明,--apple-use-keychain 在添加身份时把口令一并存入钥匙串。注意这是 macOS 特有的选项,旧版本叫 -K,现在已改名。

让它每次登录自动生效,编辑 ~/.ssh/config:

Host *
  UseKeychain yes
  AddKeysToAgent yes
  IdentityFile ~/.ssh/id_ed25519

UseKeychain yes 允许从钥匙串取口令(这是 macOS 版 OpenSSH 的选项),AddKeysToAgent yes 在使用时自动把密钥加进代理,IdentityFile 指定默认用哪把私钥。

~/.ssh/config 的权限要收紧:

sh
chmod 600 ~/.ssh/config

不同服务用不同密钥 ​

Host github.com
  IdentityFile ~/.ssh/id_ed25519_github

Host myserver
  HostName 192.168.1.50
  User myname
  IdentityFile ~/.ssh/id_ed25519_home

Host 是你在命令行里输入的别名,HostName 是真实地址,User 是登录用户名。配好之后 ssh myserver 就够了,不用记地址和用户名。

一个密钥泄露时,只需要在对应的一处服务上撤销,不必更换全部。

Git 的凭据 ​

macOS 自带的 Git 带有钥匙串凭据助手:

sh
git config --global credential.helper osxkeychain

--global 表示对所有仓库生效,credential.helper 指定用哪个助手保存凭据。配好之后,HTTPS 方式推送时输入一次用户名和令牌,之后由钥匙串接管。

用 SSH 方式(git@github.com:...)的话走的是上面的 SSH 密钥,不需要这项配置。

别把密钥提交上去 ​

在全局忽略文件里加上常见的凭据文件名:

sh
git config --global core.excludesfile ~/.gitignore_global

core.excludesfile 指定一个全局忽略规则文件。在 ~/.gitignore_global 里写:

.env
.env.*
*.pem
*.key
id_rsa
id_ed25519
.netrc

这是一层保险,不是保证——真正的保证是提交前看一眼 git diff --cached。相关清单见dotfiles 管理。

密码管理器 ​

钥匙串适合本机与开发凭据。需要跨设备、与他人共享、或者保存恢复密钥这类东西时,用专门的密码管理器。

选择时看三点:是否端到端加密(服务商自己也读不到)、是否能导出(哪天不想用了能不能带走数据)、恢复机制是什么(主密码丢了会怎样)。

无论用哪个,有两类东西必须存进去:

  1. 加密备份的仓库密码——丢了备份就永久打不开,见加密增量备份。
  2. FileVault 恢复密钥——见账户与 FileVault。

这两样都没有任何找回途径。

检查结果 ​

sh
ls -la ~/.ssh

-a 显示隐藏文件。私钥的权限应当是 -rw-------(600)。权限太松时 ssh 会直接拒绝使用该密钥。

sh
ssh-add -l

-l 列出当前代理里已加载的密钥。能看到你的密钥,说明口令已经生效,不会再反复提示。

sh
security find-generic-password -s "我的服务" -w

能取出刚才存的密码,说明命令行读写钥匙串可用。

动手练习 ​

建立一套凭据,验证它不以明文存在:

  1. 生成一对测试密钥:ssh-keygen -t ed25519 -f ~/.ssh/id_test -C "test",设一个口令(-f 指定输出文件名)。
  2. ssh-add --apple-use-keychain ~/.ssh/id_test,输入口令。
  3. ssh-add -l 确认它已加载。
  4. 用 security add-generic-password 存一条测试密码,再用 find-generic-password -w 取出来。
  5. 尝试 grep -r "你设的测试密码" ~/.ssh ~/.zshrc 2>/dev/null,确认搜不到明文。
  6. 清理:ssh-add -d ~/.ssh/id_test,rm ~/.ssh/id_test*,security delete-generic-password -s "测试服务"。

成功标准是第 5 步——凭据能用,但在文件系统里搜不到明文。这是判断一套凭据方案是否合格的最直接办法:如果你能 grep 到自己的密码,别人也能。

返回数据、备份与远程