跳到主内容

WordPress 7.2 Secrets API 详解:用 libsodium 加密存凭据,密钥随站点轮换

100%
WordPress 7.2 Secrets API 详解:用 libsodium 加密存凭据,密钥随站点轮换

WordPress 7.2 引入 Secrets API,用于把 API Key 、 OAuth 客户端密钥等凭据以加密方式存储,并保留”上一版本”以便平滑轮换。它解决了长期困扰插件作者的一个问题:敏感凭据要么明文存进 options 表,要么自己造轮子加密。现在核心提供了一套统一的、经过安全评审的方案。

它解决了什么

过去插件要存密钥,常见做法是塞进 wp_options,或者塞进 wp-config.php 的常量。前者明文风险高,后者不好版本化与轮换。 Secrets API 把凭据用 libsodium(XChaCha20-Poly1305) 加密后存入 options 表,根密钥再由 WP_SECRETS_KEY 加密保存;写入新值时会把当前值移到 WP_Secret_Version::PREVIOUS,轮换期间可回退。

核心函数

插件用下面这组函数读写密钥:

  • wp_set_secret( 'plugin-slug/secret-name', $value ):写入;
  • wp_get_secret( 'plugin-slug/secret-name' ):读取,返回 WP_Secret 实例、null 或 WP_Error;
  • wp_delete_secret():删除;
  • wp_list_secrets():只返回名称与元数据,绝不返回值;
  • wp_retire_secret_version():轮换完成后删掉上一版本;
  • wp_import_option_as_secret():把已有 option 复制成 secret(标记需轮换);
  • wp_set_network_secret() 等 *_network_secret():多站点网络级存储。
「 WP_Secret

WP_Secret 在被打印、记录日志、 JSON 编码或 var_dump() 时只显示 [secret:plugin-slug/secret-name] 占位;序列化或克隆它会抛异常。只有 reveal() 返回值,且不要在别处缓存该对象。

命名规则与读取语义

密钥名形如 plugin-slug/secret-name,只能包含小写字母、数字、连字符与下划线;带斜杠的前缀防止插件互相覆盖,但它不是访问控制——站点上任何能运行的代码都能读任意密钥。不带斜杠也能用,但会触发 _doing_it_wrong()。注意斜杠只能在中间出现一次,且不能以下划线或连字符开头或结尾。

读取时要区分三种情况:wp_get_secret() 仅在”从未存储”时返回 null;若密钥存在但当前因密钥或数据库不可用而无法读取,返回 WP_Error。两者别混:出错时去重新索要凭据,可能覆盖掉一个仍然有效的密钥。


    步骤一:写入前先用 wp_is_secrets_api_available() 判断环境是否可用(缺 WP_SECRETS_KEY 、 drop-in 加载失败、或密钥无法解密根密钥都会返回 false)。
    步骤二:用 wp_set_secret( 'my-plugin/api-key', $api_key ) 写入;用 is_wp_error() 判断失败,失败时保留旧存储,不要落到 option 。
    步骤三:读取时用 wp_get_secret(),对 WP_Secret 调 reveal() 取值;对 null 才向用户索取,对 WP_Error 则保持现状。
    步骤四:若主机以只读方式提供密钥,先 wp_secrets_provider_is_writable() 判断,不可写就显示提示而非输入框。
    步骤五:写完在 Site Health 的安全项确认 Secrets API key source 健康,再上线。

配置 WP_SECRETS_KEY

没有可用的 WP_SECRETS_KEY 时 API 什么都不存,任何读写都返回 secret_key_unavailable 错误。新安装会在安装阶段和盐值一起自动写入;已有站点需手动在 wp-config.php 的盐值下方添加:

define( 'WP_SECRETS_KEY', '…一段 base64 编码的 32 字节随机密钥…' );

生成方式:php -r 'echo base64_encode( random_bytes( 32 ) ), "\n";'。 Site Health 会给出可直接复制的行。注意:密钥不像盐值——一旦已存密钥后再改或删它,所有密钥都会读不出来,直到找回原值。

主机可托管密钥(drop-in)

主机可通过 wp-content/secrets.php 替换 API 的某一部分:WP_Secrets_Keyring(把根密钥放进 KMS 或 HSM)、WP_Secrets_Store(把加密记录存到 options 表之外)、WP_Secrets_Provider(整库交给外部密钥管理器)。 drop-in 里用 #[\SensitiveParameter] 标注接收密钥或值的参数,并在加载失败时由 Site Health 报出——API 不会回退到默认实现,避免”配置错误的后端”伪装成”空存储”。

同批 7.2 的其它安全与开发者改动,可看 WordPress 输入校验的保守策略 与 WordPress 7.2 Beta 4。

常见问题


options 明文存储、任何人可读;transients 用于缓存且同样不加密。 Secrets API 专为敏感凭据设计:加密落库、保留上一版本便于轮换、读写语义明确区分未存与存在但不可读,且对象不会在日志里泄漏明文。

只要已存过密钥,改掉或删除 WP_SECRETS_KEY 会让所有密钥暂时不可解密。需要先把它加回原值,或按官方替换密钥环、存储、提供方流程用 WP_SECRETS_KEY_PREVIOUS 平滑迁移。

能。站点级与网络级函数各自独立,不会互相回退;一次网络级轮换即可覆盖整个多站点网络。

Beta 阶段若没有 WP_SECRETS_KEY,密钥是用旧的回退密钥加密的,升级后 Site Health 会提示无法解密。按官方步骤:生成新密钥并临时用 WP_SECRETS_KEY_PREVIOUS 指向旧值,重加密根密钥后再移除旧行即可,无需重写任何已存密钥。
「延伸:Beta

自 Beta 1 起:WP_SECRETS_KEY 改为必填(不再用 LOGGED_IN_KEY 与 SALT 派生);keyring 、 store 、 provider 由接口改为抽象类(drop-in 需把 implements 改成 extends 并在 7.2 同时部署,否则每次请求都致命错误);另有多处细节修复,如 secrets.php 进入 Plugins 与 Site Health 列表、拒绝尾部换行命名、数据库读取失败不再覆盖根密钥等。
这篇有帮助吗?
云上的幻象
云上的幻象查看主页

七彩云博客,分享 WordPress 建站实战与 AI 工具测评,覆盖服务器运维、站长工具、软件资源与电商运营干货,专注原创实用的主题插件、网站加速与安全优化教程。

1061文章4评论

相关文章

评论 (0)

欢迎你,新朋友,感谢参与互动!文明发言,理性交流 · 首次评论将在审核后展示