site logo

Marico's space

REST API 认证:Cookie+Nonce 与 应用密码

编程技术 2026-10-06 17:33:49 7

WordPress 的 REST API(之前有篇文章专门介绍过)混合了两类接口:一类是公开的,任何人不用登录就能读;另一类直接拒绝未登录的请求。在 wp-admin 后台写文章时,浏览器其实一直在悄悄调用这个 API——自动保存草稿、块编辑器后台更新帖子,但你从来没让我重复输入密码。这篇来聊聊这套机制:Cookie+Nonce 认证,以及另一个用途完全不同的东西——应用密码。

wp-admin 里是什么在认证请求

登录 WordPress 后,浏览器会收到一个认证 Cookie,名字叫 wordpress_logged_in_*。之后浏览器访问同一个域名下的任何地址,都会自动带上这个 Cookie,服务端只需检查它就能知道"谁登录了"。wp-admin 自己的 JavaScript 调用 REST API——自动保存草稿、块编辑器后台更新帖子——走的就是这个 Cookie。

注意:CSRF(跨站请求伪造)是一种攻击方式。攻击原理是:当你登录了某个网站,如果不小心打开了一个恶意页面,这个页面会在你不知情的情况下,用你的浏览器 Cookie 向那个网站发请求——比如删掉一篇文章或者改个设置。因为浏览器会自动带上 Cookie,单靠 Cookie 根本无法证明这个请求是你主动发起的。

这正好就是 CSRF 钻的空子。Cookie 自动发送,登录状态下打开恶意页面,Cookie 就跟着去了。如果 REST API 只靠验证 Cookie 就允许写请求,那登录状态下打开恶意页面就能触发删除文章、改设置这些操作。

Nonce 是怎么补上这个漏洞的

Nonce 就是用来堵这个漏洞的。别被名字骗了——虽然叫"用一次",WordPress 的 Nonce 实际上是跟当前登录会话绑定的令牌,在固定时间窗口内有效(默认 24 小时,轮换后最长约 48 小时),而且绑定到特定的操作。服务端用 wp_create_nonce('some-action-name') 生成一个,嵌入到 wp-admin 页面里的 HTML/JS 中。调用 REST API 写请求时,需要在 X-WP-Nonce 请求头里带上这个值,服务端用 wp_verify_nonce() 校验它是否与当前登录用户匹配。

恶意外部网站根本拿不到这个 Nonce——它只存在于 wp-admin 页面本身,而 wp-admin 在同一个源(origin)上。所以即使攻击者页面盗用了你的 Cookie,Nonce 校验也会把它拦下来。简单说:Cookie 回答"你是谁",Nonce 回答"这个人真的是从当前页面、现在这一刻发出的这个操作吗"。wp-admin 里的 REST 请求必须两个都通过才能放行。

Cookie+Nonce 的局限

这套设计的前提是请求必须从同一个浏览器、在同一个 wp-admin 会话里发出来。反过来说,对那些根本不需要浏览器的程序——命令行工具、跑在其他服务器上的脚本——它就不太好使了。Cookie 绑定的是浏览器的登录会话,外部程序没有办法自然地获取和维持一个 Cookie;Nonce 也是同理,它存活时间短,而且每次都要重新从 wp-admin 页面里拿。

应用密码:另一扇门

应用密码(Application Passwords)是 5.6 版本(2020 年)引入的,正是为解决这个需求——让外部程序能安全调用 REST API。它跟 Cookie+Nonce 是完全独立的两套机制。底层原理类似 HTTP Basic 认证。在 wp-admin 的个人资料页面,用户可以生成一个新的应用密码,给它起个名字标记是用在哪个应用上。生成出来的是一个随机密码,跟登录密码独立;把它放在 Authorization: Basic 请求头里发出去,就带着这个账户的同等权限。

跟 Cookie+Nonce 相比有两个关键区别。第一,它不依赖任何浏览器会话状态——只要拿到了密码,任何程序立刻就能用。第二,每个应用密码都可以独立撤销。不需要把真正的登录密码交给外部集成;如果哪天不想用了,直接在 wp-admin 里撤销那一个应用密码就行,登录密码纹丝不动,其他已连接的工具也不受影响。

一个根本不需要认证的真实案例

有意思的是,我们自己 LP 上的"博客文章"区块——用 blog_latest.php 脚本实现的——这两个机制都没用。它只调用 /wp/v2/posts,这是一个只读的公开文章接口,本来设计就是任何人都能无需认证访问的。REST API 认证只有在需要获取非公开数据时才变得必要——比如拉取草稿、创建或更新文章、改设置。三层逻辑理清楚:公开数据谁都能读、登录用户才能写、外部程序通过专用应用密码写——就能很清楚地判断某个任务到底该用哪种认证方式。

总结

wp-admin 内部的请求靠两层检查——Cookie(验证登录身份)+ Nonce(验证操作确实是从当前页面、此刻发起的)——来阻挡 CSRF,同时让你不用每次请求都重新输密码。但这套设计高度依赖浏览器会话状态,自然没法延伸到外部程序调用 REST API。应用密码填补了这个空白,用一个独立的令牌机制,可以独立生成和撤销,不需要动用登录密码。同一个说法"REST API 认证",请求是从浏览器里发出来的还是从外部发出来的,调用的机制完全不同——以后决定某段代码该用什么方式认证时,脑子里要有这根弦。