关于 mcp-authprobe
mcp-authprobe 是用于主动测试自有或获准测试的远程 MCP 服务 OAuth 授权安全性的工具。它通过真实授权流程检查令牌、PKCE、重定向等行为,并输出 JSON、SARIF 和 Markdown 报告。
主要功能
- 01执行 19 项 OAuth 主动检查
- 02验证令牌轮换与重放防护
- 03检查 PKCE、重定向和受众
- 04以交互式登录获取测试授权
- 05生成 JSON、SARIF 和 Markdown 报告
- 06提供 CLI、MCP 服务和 GitHub Action
适用场景
- 01审计自有 MCP 服务的授权配置
- 02检查刷新令牌重放和轮换行为
- 03在 CI 中生成授权安全报告
- 04通过本地实验环境试跑检查
MCP 相关
该项目既可作为 MCP 服务供 AI 助手运行授权审计,也可通过 CLI 或 GitHub Action 使用。主动检查会改变目标状态,因此仅针对已证明所有权或获准测试的服务器运行。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
npx -y mcp-authprobeMCP_AUTHPROBE_SECRET_FILE密钥Path to the ownership secret (create it with `mcp-authprobe ownership init <host>`). Without a secret, only discovery runs.MCP_AUTHPROBE_SECRET密钥Ownership secret, if not read from MCP_AUTHPROBE_SECRET_FILE.MCP_AUTHPROBE_CREDENTIALS_FILE密钥Path to a test grant written by `mcp-authprobe login`, for the refresh token checks. It is consumed by a scan.MCP_AUTHPROBE_ALLOW_PRIVATE密钥Set to 1 to allow loopback and private targets (local labs only).MCP_AUTHPROBE_LAB_ORIGINS密钥Comma-separated lab origins exempt from the ownership proof, e.g. http://127.0.0.1:8080.MCP_AUTHPROBE_TIMEOUT_MS密钥Per-request timeout in milliseconds (default 10000).MCP_AUTHPROBE_BEARER_FILE密钥Path to a static API key (one line, mode 600) for a server authenticated without OAuth. Needs MCP_AUTHPROBE_BEARER_TARGET.MCP_AUTHPROBE_BEARER_TARGET密钥The exact MCP URL the static key may be used for.MCP_AUTHPROBE_BEARER_HEADER密钥Header for the static key (default Authorization, sent as Bearer).本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。
工具
5收录的工具列表 · 更新于 今天
此工具无需参数。
详细介绍
mcp-authprobe
对你拥有的远程 MCP 服务进行主动授权测试。
被动扫描器会读取远程 Model Context Protocol 服务声明的内容:它的 OAuth 元数据、TLS 配置和工具描述。有些属性只能通过实际测试授权服务来确认:它是否真的会轮换刷新令牌,是否真的会拒绝未注册的重定向 URI,是否会强制执行 PKCE,是否会接受面向其他服务的令牌,以及是否会对重复失败进行限速?
mcp-authprobe 会对你已证明拥有所有权的服务运行这些主动检查,并以 JSON、SARIF 和 Markdown 格式报告结果。
19 项检查,已通过认证的 OpenID 提供方实测:OAuth 发现、刷新令牌轮换与重放、重定向 URI 严格性、PKCE 强制执行、一次性授权码、令牌受众、
Origin验证、URL 中的令牌、用作凭据的会话 ID、CORS、限速和(在真实浏览器中进行的)同意流程 CSRF。它既可作为带交互式login的 CLI 使用,也可作为 MCP 服务让 AI 助手运行审计,还可用作 GitHub Action。精心设计的易受攻击实验环境让你可以在一分钟内尝试所有功能。
**仅测试你拥有或已获明确授权测试的服务。**主动检查会改变目标上的状态。此工具会在运行检查前强制验证所有权证明,但责任仍由操作者承担。

目录
- 实测验证
- 为什么还要另一个扫描器?
- 安装
- 一分钟上手:实验环境
- 快速开始
- 所有权证明
- 刷新令牌检查
- 交互式登录
- 重定向 URI 检查
- 限速检查
- 同意流程 CSRF 检查
- 从 MCP 客户端使用
- 检查内容
- 与 mcp-posture 结合使用
- 报告和退出码
- GitHub Action
- 扫描器的安全性
- 路线图
- 开发
- 贡献与安全
实测验证
除了自带的测试套件和实验室环境,每次发布前都会使用真实的授权栈测试 mcp-authprobe:
- node-oidc-provider(通过 OpenID 认证的授权服务器)支持动态注册、资源指示符和 JWT 访问令牌。它前面部署了一个使用官方 TypeScript SDK 构建的 MCP 服务,用于验证令牌受众和
Origin标头。所有可从外部判断的检查都已通过;MAP-THR01按预期未通过,因为该提供程序将速率限制交由部署方处理;其同意页面使用每个流程的标识符而非反 CSRF 字段保护,因此报告为无法确定,并附有原因。 - MCP TypeScript SDK 的 OAuth 示例服务,这是一个不适用于生产环境的演示。结果与其代码相符:
redirect_uri未与授权码绑定、不验证Origin、注册时接受不安全的重定向 URI、不提供刷新令牌;崩溃会报告为无法确定,而非通过。其会话会正确要求每个请求都提供令牌(MAP-SES01通过)。
这些测试促成了多项修复:服务器不发放刷新令牌或在登录期间崩溃时仍保留结果;能够应对损坏的刷新授权的限流检查;以及上述每流程标识符分析。
为什么还需要另一个扫描器?
已有不少优秀工具,而这个工具旨在与它们配合使用,而非取而代之:
| 工具 | 重点 | 与 mcp-authprobe 的重叠 |
|---|---|---|
| mcp-posture | 远程服务的被动安全态势检查:符合 RFC 的 OAuth 元数据、传输、工具投毒、rug pull | 设计上没有重叠:它不会发送会改变状态的请求。请先运行它。 |
| MCPJam OAuth conformance | 执行真实 OAuth 流程并检查合规性,包含少量负向检查 | 基本的重定向 URI 拒绝检查。不检查刷新令牌重放、限流或同意页面 CSRF。 |
| Cisco mcp-scanner、Snyk agent-scan | 恶意工具、配置、本地服务 | 没有重叠。 |
mcp-authprobe 专注于只有通过主动授权测试才能确认的问题,并使用不会与 mcp-posture 的检查 ID(MCPP-…)冲突的检查 ID(MAP-…)。mcp-posture 为此预留了自己的主动检查 ID,并指向此处;两份报告可以合并为一份。
安装
需要 Node.js 22.19 或更高版本。
# run without installing
npx mcp-authprobe --help
# or install globally
npm install -g mcp-authprobe
发布版本通过 GitHub Actions 使用 npm 可信发布方式发布;安装安全工具前,请在软件包页面检查其来源证明。
从源代码安装:
git clone https://github.com/Enigmacker/mcp-authprobe.git
cd mcp-authprobe
npm ci
npm run build
node dist/cli.js --help
一分钟上手:实验室
mcp-authprobe lab 会在 127.0.0.1 上启动一个带有独立 OAuth 授权服务器的小型 MCP 服务,让你可以查看每项检查的运行情况,而无需接触真实服务:
# Terminal 1: a deliberately vulnerable server (or --profile secure)
npx mcp-authprobe lab
# Terminal 2
npx mcp-authprobe scan http://127.0.0.1:8787/mcp --allow-private
npx mcp-authprobe login http://127.0.0.1:8787/mcp --allow-private --out lab-grant.json
npx mcp-authprobe scan http://127.0.0.1:8787/mcp --allow-private --credentials-file lab-grant.json
使用账号 test / test 登录,然后点击 允许。
| 配置 | 预期结果 |
|---|---|
vulnerable(默认) | 接受刷新令牌重放、注册不安全的重定向 URI、不强制执行 PKCE、忽略令牌受众、接受外部 Origin、接受 URL 中的令牌、无需令牌即可接受会话 ID、允许任何来源携带凭据进行 CORS、没有限流、同意页面没有 CSRF 保护。MAP-RDR02 报告为无法确定:该服务会先将未知访问者重定向到登录页面,再检查 redirect_uri;从外部无法区分这种情况,而扫描器绝不会将其判定为通过。 |
secure | 所有检查均通过(或不适用);MAP-CSR01 使用 consent-check 运行。 |
实验室仅监听 127.0.0.1 或 ::1(--host、--port,默认端口为 8787;拒绝 localhost 等名称,避免解析器将其指向其他地址),所有数据仅存储在内存中,并可通过 Ctrl-C 停止。其访问令牌是未签名的 JWT,因此 MAP-AUD01 可以读取令牌受众。该实验室有意设置为易受攻击:切勿将其暴露出去。
快速开始
# 1. Create a secret and the proof to publish for your MCP host
mcp-authprobe ownership init mcp.example.com
# 2. Publish the proof (well-known file or DNS TXT), then keep the secret in your environment
export MCP_AUTHPROBE_SECRET='…'
# 3. Check the proof is visible
mcp-authprobe ownership verify mcp.example.com
# 4. Scan
mcp-authprobe scan https://mcp.example.com/mcp --sarif results.sarif --markdown summary.md
# 5. Log in once with a dedicated test account to create a test grant
mcp-authprobe login https://mcp.example.com/mcp --out ./test-grant.json
# 6. Full scan, including the refresh token checks
mcp-authprobe scan https://mcp.example.com/mcp --credentials-file ./test-grant.json
如果没有有效的证明,扫描仍会执行发现(也就是任何 MCP 客户端在登录前都会发送的相同只读请求),并将每项主动检查报告为 skipped。
在你自己的机器上搭建本地实验环境:
mcp-authprobe scan http://127.0.0.1:8080/mcp --allow-private
使用 --allow-private 时,环回目标无需提供证明。
所有权证明
主动检查会注册客户端、轮换令牌并发送失败请求。为确保它们只会访问其运营者已同意的服务器,扫描器要求每个它可能联系的主机都提供证明:MCP 服务主机,以及在不同情况下的授权服务器主机。
证明是一个与主机绑定的哈希,源自只有你持有的秘密:
mcp-authprobe-verification=sha256:<hex(sha256("<host>:<secret>"))>
可通过以下两种方式之一发布:
| 方法 | 位置 | 值 |
|---|---|---|
| Well-known 文件 | https://<host>/.well-known/mcp-authprobe.txt | 每个证明占一行;忽略其他行 |
| DNS TXT | _mcp-authprobe.<host> | 证明字符串 |
设计选择:
- 秘密不会离开你的机器。 发布的只有其哈希,因此即使有人在公开渠道找到证明,也无法利用它扫描你的服务器。
- 证明与主机绑定。
mcp.example.com的证明对其他任何主机都无效。 - 秘密从
MCP_AUTHPROBE_SECRET或--secret-file读取,绝不会从命令行读取,因此不会出现在 shell 历史记录或进程列表中。 - 托管的授权服务器。 如果你的授权服务器是使用独立域名的第三方服务,只有在你控制该域名(例如使用自定义域名)时才能发布证明。对其运行主动检查前,请查看提供商的渗透测试政策。
测试完成后即可移除证明。
刷新令牌检查
MCP 客户端通常是公共客户端,因此 OAuth 2.1 要求其刷新令牌必须轮换;RFC 9700 建议在重用旧令牌时撤销整个令牌族。这些属性无法通过元数据读取,必须实际验证。
这些检查需要一个测试授权:通过使用你自己的服务器登录专用测试账户获得的客户端和刷新令牌。
最简单的方式是使用 mcp-authprobe login(参见交互式登录),它会为你写入文件。你也可以手动写入,并确保只有你自己可读(chmod 600):
{
"client_id": "your-test-client-id",
"refresh_token": "the-refresh-token",
"client_secret": "only-for-confidential-clients",
"token_endpoint_auth_method": "none"
}
token_endpoint_auth_method 可设为 none(公共客户端的默认值)、client_secret_post(存在密钥时的默认值)或 client_secret_basic。
扫描最多会向发现过程中找到的令牌端点发送三次 refresh_token 请求:
| 步骤 | 请求 | 符合规范的服务器应有的响应 | 检查 |
|---|---|---|---|
| 1 | 使用提供的令牌刷新 | 返回新的访问令牌和新的刷新令牌 | MAP-RTK01 |
| 2 | 使用步骤 1 输入的令牌再次刷新(该令牌已用过) | 拒绝请求,通常返回 invalid_grant | MAP-RTK02 |
| 3 | 使用最新令牌刷新(步骤 1 的输出) | 拒绝请求:重用令牌已撤销整个令牌族 | MAP-RTK03 |
重要提示:
- 此授权会被消耗。 对符合规范的服务器,步骤 3 会撤销该授权。切勿使用仍需保留的授权。
- 如果令牌没有轮换,
MAP-RTK01将失败,且两项重放检查会报告为不适用。 - 如果响应被限流或出现意外情况,会报告为
error(结论不确定),绝不会报告为通过。 - 输出或报告中绝不会出现令牌值;证据只显示状态码和 OAuth 错误码。
交互式登录
mcp-authprobe login https://mcp.example.com/mcp --out ./test-grant.json
login 的行为类似谨慎的原生 MCP 客户端(RFC 8252):
- 它会通过动态客户端注册来注册客户端,并使用
127.0.0.1上随机端口的环回重定向 URI。 - 它只会在浏览器中打开发现的授权端点(或使用
--no-browser时打印 URL)。请使用你的测试账号登录。 - 环回监听器只接受携带预期
state的单个GET /callback,且不会向其返回任何内容。 - 它使用 PKCE S256、随机
state、RFC 8707resource,并会将 RFC 9207iss参数与发现的发行者进行核对。 - 它会以 600 权限写入授权信息,绝不通过符号链接写入,也绝不覆盖现有文件,除非你传入
--force。令牌绝不会被打印出来。 - 授权信息会记录其所属的目标和令牌端点。扫描其他目标,或服务器公布了其他令牌端点时,都不会使用它。
login本身不需要所有权证明:它只会执行任意 MCP 客户端连接时会做的事情(注册一次、在你自己的浏览器中登录一次、交换一次授权码)。只有下面的额外登录检查需要所有权证明。- 如果登录失败,只要服务器允许,客户端就会删除它注册的客户端。
登录期间会执行七项检查,因为这些检查需要新的授权码或有效的访问令牌。只有在已证明每个被联系主机的所有权时才会运行(--no-probe 会跳过这些检查),顺序如下:
| 检查 | login 的操作 | 失败条件 |
|---|---|---|
MAP-PKC01 | 先在不带 code_verifier 的情况下提交授权码,再使用错误的 code_verifier 提交 | 令牌端点签发了令牌 |
MAP-RDR03 | 使用不同的 redirect_uri 提交授权码 | 令牌端点签发了令牌 |
| (交换) | 正常交换授权码 | |
MAP-ORG01 | 使用访问令牌发送 initialize(控制请求,必须成功),然后使用 Origin: https://attacker.mcp-authprobe.invalid 发送相同请求,再发送一次控制请求 | 外部请求被当作普通请求处理。只有在 403 后令牌仍然有效时才通过 |
MAP-TOK01 | 使用 ?access_token= 中的令牌发送 initialize,且不带 Authorization 标头,然后再发送一次控制请求 | URL 中的令牌被接受(MCP 规范禁止这样做:URL 会出现在日志、历史记录和 Referer 标头中)。令牌绝不会出现在报告中 |
MAP-SES01 | 使用令牌建立 MCP 会话,使用会话 ID 和令牌发送 ping(控制请求),然后仅使用会话 ID 发送,再结束会话 | 不带令牌的请求被处理。对于不分配会话 ID 的无状态服务器,此项不适用 |
MAP-AUD01 | 使用刷新令牌请求一个针对 https://other.mcp-authprobe.invalid/mcp 的令牌,并将其提交给你的 MCP 端点(仅在控制请求成功后执行) | MCP 端点接受了受众为其他资源的令牌(从 JWT aud 读取)。如果授权服务器返回 invalid_target,或 MCP 端点返回 401/403,则通过。如果令牌是不透明令牌,或授权服务器忽略了 resource 并签发了针对你服务器的令牌,则结果无法判定,并附有说明,因为不能归咎于 MCP 服务器 |
MAP-COD01 | 再次提交已使用过的授权码,然后尝试一次最新的刷新令牌 | 授权码再次被交换。对于重用授权码签发的令牌,若撤销这些令牌(刷新时返回 invalid_grant),则会报告为建议做法;其他任何响应都意味着撤销状态未知 |
结果会保存在授权信息文件中,并由下一次针对同一目标的扫描报告。授权信息中保留的刷新令牌始终是最新的,因此这些探测期间的令牌轮换不会影响扫描。探测期间发生网络故障时,该项检查会被标记为无法判定;授权信息和其他结果都不会丢失。
如果探测获取到了令牌(即检查失败),授权码就会被消耗:后续的交换前探测会报告为未观察到,获取的令牌会保留为授权信息,而 MAP-COD01 仍会运行(再次提交该授权码即为第二次使用)。
一些严格的服务端会在任何一次失败尝试后使代码失效。这种行为可以接受:只有第一次拒绝结果可信,后续探测均报告为无法判定。同样,在代码重用后撤销令牌(建议如此)的服务端也能通过 MAP-COD01。在这两种情况下,login 都会在没有授权许可的情况下写入结果;使用 --no-probe --force --out <same file> 重新登录以添加授权许可:同一目标的先前结果会保留。
重定向 URI 检查
如果授权服务将请求重定向到客户端从未注册的 URI,授权代码可能落入攻击者手中。OAuth 2.1 要求进行精确字符串匹配。这些检查要求服务器支持动态客户端注册;无需测试授权许可,也无需登录。
工作原理:
- 扫描器会注册一个临时客户端,其唯一的重定向 URI 为
https://client.mcp-authprobe.invalid/callback。 - 扫描器先发送带有已注册 URI 的基准授权请求(PKCE S256、随机
state、RFC 8707resource),然后针对每个变体各发送一个请求,并比较响应。扫描器不会跟随重定向。 - 扫描器会删除创建的每个客户端(RFC 7592)。无法删除的客户端会列在报告中,名称为
mcp-authprobe test client (safe to delete)。
| 检查 | 发送内容 | 失败条件 |
|---|---|---|
MAP-RDR01 | 完全不同的主机 | 服务端将请求重定向到该主机 |
MAP-RDR02 | 7 种近似但不匹配的情况:额外路径段、尾部斜杠、添加查询参数、不同端口、子域名、userinfo 技巧、降级为 http | 服务端将请求重定向到其中任意一种 |
MAP-RDR04 | 注册使用非回环主机的纯 HTTP URI、javascript:、data: 和带片段的 URI | 注册端点接受其中任意一种 |
安全性设计:所有使用的主机都位于保留的 .invalid 顶级域名下(RFC 2606),因此永远无法解析。即使服务端存在漏洞,也无法将代码发送到任何地方。
结果说明:如果某个变体的响应与基准完全相同(例如两者都重定向到登录页面),则报告为无法判定(error),而不是通过,因为有些服务端只有在用户登录后才会检查 redirect_uri。MAP-RDR03(令牌端点强制执行)会在交互式登录期间检查。
限流检查
如果没有速率限制,刷新令牌、授权代码和客户端密钥就可能被快速穷举。MAP-THR01 检查令牌端点是否会减缓重复失败请求,并设有严格上限:
- 默认发送 10 个令牌请求(
--throttle-attempts,范围为 5 到 20,并设有硬性上限),每次只发送一个。 - 每个请求都携带一个新生成的随机刷新令牌,该令牌不可能有效。不会猜测任何内容,且绝不会涉及用户密码和登录表单。如果第一个请求就收到服务器错误或
unsupported_grant_type(表示服务器没有可用的刷新授权),检查会切换一次,改用随机授权代码,并重新开始计数。 - 如果提供了授权许可,则使用你的测试客户端(不会发送你的真实刷新令牌);否则使用一个之后会删除的临时客户端。
- 一旦出现限流迹象,就会停止:HTTP 429、
Retry-After标头,或 OAuthslow_down/temporarily_unavailable错误。 - 扫描中会将此检查放在最后运行,以免令牌端点受到限流后影响其他检查。
失败表示在上限范围内未发现限流。你的限制可能只是高于 20;此项发现会提示你确认是否存在限流。
Consent CSRF 检查
如果第三方网站能代表已登录用户提交同意页面,就能在用户毫不知情的情况下让其授予访问权限(这是 MCP 安全最佳实践中所述的困惑代理问题)。MAP-CSR01 会在真实浏览器中使用你的测试账号检查同意决定是否受到保护:
npx -p mcp-authprobe -p playwright-core mcp-authprobe consent-check https://mcp.example.com/mcp
- 必须先证明每个被访问主机的所有权。
- 它会注册一个临时客户端,并在你已安装的 Chrome(或使用
--browser msedge,或--browser-path <executable>)中打开授权页面。不会下载浏览器;playwright-core是可选依赖,仅供此命令使用。 - 使用测试账号登录。显示同意页面时,不要点击任何内容:返回终端并按 Enter。
- 工具会读取同意表单和服务器设置的 Cookie 属性,然后关闭浏览器,再使用会话 Cookie 提交表单:
- 不带任何反 CSRF 字段、从外部 Origin 提交:必须拒绝;
- 修改反 CSRF 字段(保持长度和字符集不变)、从外部 Origin 提交:必须拒绝;
- 未改动的表单、从外部 Origin 提交:仅供参考,用于显示服务器是否也会检查 Origin;
- 对照项:从页面自身的 Origin 提交未改动的表单,必须接受。
- 工具会删除临时客户端。回环重定向 URI 由工具自身提供服务,因此即使意外批准,授权码也不会被发送到其他地方。
许多授权服务器(node-oidc-provider、Ory Hydra、Keycloak)没有反 CSRF 字段:同意表单会携带由服务器签发的单次流程标识符,位于隐藏字段中或表单 URL 中(/interaction/<id>)。如果没有反 CSRF 字段,但有此类标识符,则会先额外提交两次,之后才能授予任何权限:一次修改标识符,另一次不带任何 Cookie,且两次都从外部 Origin 提交。如果任一请求被接受,检查就会失败。如果两次都被拒绝,且未改动的表单被接受,结果将为无法判定,并附带说明:如果标识符与用户自己的会话绑定,这种设计就是安全的;但无法通过单个会话证明这一点。
如果未提供有效的反 CSRF 令牌就授予了同意,检查就会失败。只有当对照项证明工具能够复现真实授权时,检查才会通过;否则(例如工具无法识别标签、同意由 JavaScript 提交、存在多步流程或令牌只能使用一次),结果为无法判定,绝不会判定通过。检查结果还会报告会话 Cookie 是否显式设置了 SameSite=Lax/Strict:这会在现代浏览器中阻止来自其他站点的攻击,但无法阻止来自同站点子域或旧版客户端的攻击;未设置该属性的 Cookie 会报告为 Unspecified。按 Ctrl-C、关闭浏览器或等待 10 分钟超时,都会干净地停止检查,并仍然删除临时客户端。
此功能在 MCP 服务模式下不可用,因为它需要你登录。
在 MCP 客户端中使用
mcp-authprobe mcp 会通过 stdio 运行与 MCP 服务相同的引擎,因此任何 MCP 客户端中的 AI 助手都可以帮你审计服务。
将其添加到客户端的 MCP 配置中(大多数客户端使用此 mcpServers 格式):
{
"mcpServers": {
"mcp-authprobe": {
"command": "npx",
"args": ["-y", "mcp-authprobe", "mcp"],
"env": {
"MCP_AUTHPROBE_SECRET_FILE": "/home/you/.config/mcp-authprobe/secret",
"MCP_AUTHPROBE_CREDENTIALS_FILE": "/home/you/.config/mcp-authprobe/test-grant.json"
}
}
}
}
该服务也已列入官方 MCP Registry,名称为 io.github.Enigmacker/mcp-authprobe,因此支持 Registry 的客户端可以直接安装。
从源代码检出目录运行时,使用 "command": "node" 和 "args": ["/path/to/mcp-authprobe/dist/cli.js", "mcp"]。
| 工具 | 功能 | 注释 |
|---|---|---|
list_checks | 列出检查目录 | 只读 |
explain_check | 解释一项检查 | 只读 |
ownership_proof | 返回要为某个主机发布的证明(绝不返回密钥) | 只读 |
verify_ownership | 检查证明是否已发布 | 只读,可访问开放网络 |
scan | 运行发现和主动检查;返回 Markdown 摘要以及结构化内容格式的完整报告 | 有破坏性,可访问开放网络 |
该服务经过设计,可防止助手或传入其中的提示词注入扩大扫描器的操作范围:
- **所有敏感设置都由你在配置中指定,绝不通过工具参数传入:**所有权密钥、测试授权和私有地址访问权限。
scan工具只接受 URL,不接受其他参数。 scan被标记为具有破坏性,因此行为良好的客户端会在每次调用前请求确认。它会消耗测试授权并注册临时客户端。- 只接受
https目标;仅当设置了MCP_AUTHPROBE_ALLOW_PRIVATE=1时,环回地址或私有地址实验环境才允许使用普通http。包含内嵌凭据的 URL 会被拒绝,并且一次只运行一个扫描任务。 - 在 MCP 模式下,环回目标仍需提供证明,除非你将其来源列入
MCP_AUTHPROBE_LAB_ORIGINS。助手无法将主动请求发送到任意本地服务。 - 只有当测试授权中指定了正在扫描的目标时,才会使用该授权(由
login创建的授权符合此要求),因此无法将其用于其他服务。 - **报告会被标记为数据:**每份扫描结果末尾都会附有提示,指出其中由服务控制的文本不得视为指令。
| 环境变量 | 用途 |
|---|---|
MCP_AUTHPROBE_SECRET 或 MCP_AUTHPROBE_SECRET_FILE | 所有权密钥(建议使用文件) |
MCP_AUTHPROBE_CREDENTIALS_FILE | 用于刷新令牌检查的测试授权 |
MCP_AUTHPROBE_ALLOW_PRIVATE=1 | 允许访问环回和私有目标(仅用于本地实验环境) |
MCP_AUTHPROBE_LAB_ORIGINS | 以逗号分隔的实验环境来源(例如 http://127.0.0.1:8080),这些来源无需所有权证明 |
MCP_AUTHPROBE_TIMEOUT_MS | 单次请求超时时间 |
使用 Docker
仓库中的 Dockerfile 会构建相同的 stdio MCP 服务:
docker build -t mcp-authprobe https://github.com/Enigmacker/mcp-authprobe.git
docker run -i --rm -e MCP_AUTHPROBE_SECRET mcp-authprobe
在容器中,你机器上的 --allow-private 实验环境无法通过 127.0.0.1 访问;请在实验环境中使用 npm 包。
检查内容
mcp-authprobe checks list 会打印检查目录,mcp-authprobe checks show <ID> 会说明某项检查的重要性、修复方法和参考资料。ID 稳定且永不重复使用。
| ID | 模式 | 状态 | 检查项 |
|---|---|---|---|
MAP-DSC01 | 被动 | 可用 | 可发现授权端点(401 挑战、RFC 9728、RFC 8414) |
MAP-RTK01 | 主动 | 可用 | 公共客户端的刷新令牌会轮换 |
MAP-RTK02 | 主动 | 可用 | 已轮换的刷新令牌无法重放 |
MAP-RTK03 | 主动 | 可用 | 重放刷新令牌会撤销整个令牌族 |
MAP-RDR01 | 主动 | 可用 | 授权端点会拒绝未注册的 redirect_uri |
MAP-RDR02 | 主动 | 可用 | redirect_uri 必须精确匹配,不采用前缀或模式匹配 |
MAP-RDR03 | 主动 | 可用(通过 login) | 令牌端点会强制使用授权时采用的 redirect_uri |
MAP-RDR04 | 主动 | 可用 | 动态客户端注册会拒绝不安全的重定向 URI |
MAP-THR01 | 主动 | 可用 | 连续多次令牌请求失败会被限流(使用随机值连续请求 5 到 20 次) |
MAP-CSR01 | 主动 | 可用(通过 consent-check) | 同意决策受到 CSRF 防护 |
MAP-PKC01 | 主动 | 可用(通过 login) | 令牌端点会强制执行 PKCE(缺少 code_verifier、code_verifier 错误) |
MAP-COD01 | 主动 | 可用(通过 login) | 授权码无法重复使用 |
MAP-AUD01 | 主动 | 可用(通过 login) | 令牌与其受众绑定(RFC 8707) |
MAP-ORG01 | 被动 | 可用 | MCP 端点会拒绝来自外部 Origin 的请求 |
MAP-TOK01 | 主动 | 可用(通过 login) | MCP 端点会拒绝 URL 中的访问令牌 |
MAP-SES01 | 主动 | 可用(通过 login) | MCP 会话 ID 不能替代令牌使用 |
MAP-COR01 | 被动 | 可用 | MCP 端点不允许来自任何来源的携带凭据请求(CORS) |
类别:DSC 发现、RTK 刷新令牌、RDR 重定向 URI、THR 限流、CSR 同意 CSRF、PKC PKCE、COD 授权码、AUD 令牌受众、ORG Origin 验证、TOK 令牌传输、SES 会话、COR CORS。
每次扫描都会运行 MAP-ORG01,且无需所有权证明:它会发送两条普通的 initialize 请求,其中一条带有外部 Origin。大多数服务会先要求提供令牌,再检查 Origin;此时扫描会将其报告为无法确定,而 login 会使用有效令牌观察该行为。每次扫描也会运行 MAP-COR01:发送 CORS 预检请求和一条带有外部 Origin 的普通请求;只有当该来源被回显,且同时返回 Access-Control-Allow-Credentials: true 时,检查才会失败。
选择检查项
--only 和 --skip 接受以逗号分隔的检查 ID(MAP-RDR01 或仅写 RDR01)或类别(RDR),且不区分大小写。--skip 的优先级高于 --only;未知名称会导致用法错误,因此拼写错误不会悄然改变运行的检查项。
mcp-authprobe scan https://mcp.example.com/mcp --skip THR # production: no failed token requests
mcp-authprobe scan https://mcp.example.com/mcp --only RDR02 # re-check one fix
未选中的检查项不会发送任何请求,并会列为 skipped (not selected),因此报告不会误称该检查已通过。三项刷新令牌检查属于同一序列:选择其中任意一项都会运行全部三项(其余两项随后会报告为未选中),跳过全部三项则会使测试授权保持不变。MCP 服务的 scan 工具接受相同的 only 和 skip 参数。
与 mcp-posture 结合
mcp-posture 会读取服务声明的内容;mcp-authprobe 会测试服务强制执行的内容。先运行 mcp-posture,再将其 JSON 报告交给扫描:
mcp-posture scan https://mcp.example.com/mcp --json posture.json
mcp-authprobe scan https://mcp.example.com/mcp --posture-report posture.json --markdown report.md
报告会新增一个被动检测部分,其中包含 mcp-posture 的发现结果(及其稳定指纹),并为两个工具都会检查的每个对象各提供一行:
| 关系 | 含义 |
|---|---|
| 已确认 | mcp-posture 在元数据中发现了迹象,且主动测试证实了该弱点 |
| 仅主动检测发现 | 元数据中没有相关迹象,但主动测试证实了该弱点(例如,声明支持 PKCE,但未强制执行) |
| 已强制执行 | mcp-posture 根据元数据标记了该问题,或无法被动验证(MCPP-ASM08、令牌受众),但主动测试表明已强制执行 |
| mcp-authprobe | mcp-posture,被动检测 | 为主动测试保留的 mcp-posture ID |
|---|---|---|
MAP-DSC01 | MCPP-AUTHN01-03、MCPP-PRM01、MCPP-ASM01 | |
MAP-PKC01 | MCPP-ASM04、MCPP-ASM05 | MCPP-ACT08 |
MAP-AUD01 | MCPP-ASM08 | MCPP-ACT03、MCPP-ACT06 |
MAP-TOK01 | MCPP-PRM06 | MCPP-ACT02 |
MAP-SES01 | MCPP-TRN08、MCPP-TRN09 | MCPP-ACT04 |
MAP-ORG01 | MCPP-ACT01 | |
MAP-RDR01、MAP-RDR02 | MCPP-ACT07 | |
MAP-RDR04 | MCPP-ASM09 | MCPP-ACT09 |
报告必须是 mcp-posture 的 JSON(schema_version 为 1.x),且包含针对相同 URL 的检测结果。报告会被视为不可信输入:其大小会受到限制、类型会经过检查,并像其他所有内容一样进行净化。被抑制的 mcp-posture 发现结果会被排除,且 mcp-posture 的发现结果不会影响 mcp-authprobe 的退出代码。合并后的结果会显示在表格、JSON(posture 区块)和 Markdown 报告中;SARIF 仅保留 mcp-authprobe 的结果,因为 mcp-posture 会生成自己的 SARIF。
报告和退出代码
| 选项 | 输出 |
|---|---|
| (始终) | 在标准输出上显示易读表格 |
--json <file> | 完整报告,架构为 mcp-authprobe/report/v1 |
--sarif <file> | SARIF 2.1.0,供 GitHub 代码扫描使用;只有未通过的检查会成为结果 |
--markdown <file> | 用于拉取请求评论或 CI 作业摘要的表格 |
--credentials-file <file> | 用于刷新令牌检查的测试授权(输入) |
| 退出代码 | 含义 |
|---|---|
0 | 没有达到或超过 --fail-on(默认为 high)级别的未通过检查 |
1 | 至少有一项未通过检查达到或超过 --fail-on 级别(或 ownership verify 失败) |
2 | 用法错误 |
3 | 检查出错或目标不可访问 |
所有由服务控制的文本在进入报告前都会经过净化:终端转义序列、双向文本覆盖字符和不可见字符会被转义,类似令牌的值会被删除。
GitHub Action
在每次部署时或每晚运行扫描,以便在其他人发现问题之前,及时捕获重新引入缺陷的变更。
name: MCP authorization
on:
schedule:
- cron: "17 3 * * *"
workflow_dispatch:
permissions:
contents: read
security-events: write # only for upload-sarif
jobs:
scan:
runs-on: ubuntu-latest
steps:
- uses: Enigmacker/[email protected]
with:
url: https://staging.example.com/mcp
secret: ${{ secrets.MCP_AUTHPROBE_SECRET }}
skip: THR # optional: no failed token requests
upload-sarif: true # optional: findings in the Security tab
结果表格会添加到作业摘要中。当检查失败且严重性达到或超过 fail-on 时,此步骤会失败。
| 输入 | 默认值 | 含义 |
|---|---|---|
url | (必填) | 要扫描的 MCP 端点 |
secret | 来自仓库密钥的所有权密钥。未提供时,只运行发现、MAP-ORG01 和 MAP-COR01 | |
fail-on | high | 当检查失败且严重性达到或超过此级别时失败 |
fail-on-inconclusive | false | 检查结果不确定时也失败(否则发出警告)。无法访问目标时始终失败 |
only / skip | 与 --only / --skip 相同 | |
posture-report | 要合并的 mcp-posture JSON 报告,参见与 mcp-posture 合并 | |
throttle-attempts、timeout | 与 CLI 选项相同 | |
upload-sarif | false | 将 SARIF 报告上传到代码扫描(需要 security-events: write) |
allow-private | false | 仅用于在同一作业中启动的实验环境 |
node-version | 22 | 用于运行扫描器的 Node.js 版本 |
package | 此版本 | 要运行的 npm 软件包规格,例如 [email protected] |
两种工具都有 Action,因此可以在同一个作业中结合被动和主动视图:
- uses: batou9150/[email protected]
with:
targets: https://staging.example.com/mcp
args: --json posture.json
fail: false # let the active scan run too; each tool reports its own findings
- uses: Enigmacker/[email protected]
with:
url: https://staging.example.com/mcp
secret: ${{ secrets.MCP_AUTHPROBE_SECRET }}
posture-report: posture.json
输出:exit-code、failed(失败检查的数量)、json-file、sarif-file、markdown-file。
密钥通过环境变量传递,绝不会出现在命令行中。Action 不会运行刷新令牌检查和登录检查:这些检查需要通过交互式 login 获取测试授权,且扫描会消耗该授权。在授权服务器发生变更后,请手动运行这些检查。
只要条件允许,请在预发布环境而非生产环境中运行主动扫描。
如果不使用 Action,也可以通过普通步骤完成相同操作:
- uses: actions/setup-node@v7
with:
node-version: 22
- run: npx mcp-authprobe scan "$MCP_URL" --sarif results.sarif --markdown summary.md
env:
MCP_URL: https://mcp.example.com/mcp
MCP_AUTHPROBE_SECRET: ${{ secrets.MCP_AUTHPROBE_SECRET }}
- run: cat summary.md >> "$GITHUB_STEP_SUMMARY"
if: always()
扫描器的安全性
扫描器会与可能具有恶意的服务通信,也可能由 AI Agent 驱动。它会保护你和第三方:
- **所有权门控:**除
login外,没有有效证明就不会向任何被联系的主机发送主动请求(login的行为与普通 MCP 客户端相同;见上文)。 - **仅限 HTTPS:**每个请求(包括服务元数据中声明的端点)都必须使用 HTTPS。只有在启用
--allow-private的环回或私有地址实验环境中才接受普通 HTTP,因此令牌和密钥不会通过明文在互联网传输。 - **结果不确定绝不视为通过:**速率限制(429、
Retry-After)和服务端错误(5xx)会使检查报告为error,而非pass。 - **SSRF 防护:**连接时会拒绝环回、私有、链路本地、CGNAT、多播、保留地址和云元数据地址;传递
--allow-private时除外。检查发生在 DNS 解析之后。 - **不自动重定向:**每次重定向都会经过检查,绝不会盲目跟随。
- **资源有上限:**每个请求都有超时限制,响应正文上限为 1 MiB,且不会请求压缩。
- **密钥严格保密:**不会打印令牌和所有权密钥,并会在所有报告中将其脱敏。
- 无遥测。
路线图
0.1.0
刷新令牌检查(RTK01-03)已完成,使用测试授权文件。交互式登录并创建测试授权已完成。重定向 URI 检查(RDR01、RDR02、RDR04)已完成。已完成,在登录过程中观察到。MAP-RDR03限流检查(THR01)已完成。MCP 服务模式(stdio)已完成。npm 发布已完成。
0.2.0
同意流程 CSRF 检查(CSR01),通过浏览器会话进行已完成。
0.3.0
PKCE 强制执行(PKC01)、代码重用(COD01)、令牌受众(AUD01)、Origin 验证(ORG01)已完成。本地实践服务(已完成。lab)
0.4.0
GitHub Action、已完成。--only/--skip
0.5.0
针对 node-oidc-provider 和 MCP SDK 的 OAuth 示例进行实地测试,并完成由此产生的修复已完成。
0.6.0
URL 中的令牌(TOK01)、不带令牌的会话 ID(SES01)、带凭据的 CORS(COR01)已完成。
0.7.0
易读的终端输出和动画演示已完成。
0.8.0
导入 mcp-posture 报告,并与当前结果合并已完成。Dockerfile,并在 Glama 上列出已完成。
后续
- 欢迎在 issue 中提出想法。
开发
npm ci
npm run typecheck
npm test # unit and integration tests against a local fixture server
npm run build
npm run check # all of the above
npm run demo # re-record docs/demo/demo.gif (needs Chrome and ffmpeg)
测试不会离开本机:集成测试会针对绑定到 127.0.0.1 的固定装置服务运行。
贡献与安全
- 如何添加检查:CONTRIBUTING.md
- 报告 mcp-authprobe 本身的漏洞:SECURITY.md
- 变更:CHANGELOG.md
许可证
MIT © 2026 Enigmacker
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。