← 返回首页

WordPress 深度系列

MCPWordPress安全加固Cloudflare

把 mcp-adapter 装上之后,AI 真的可以直接读写你的站点——但同时你刚把 /wp-json/mcp/mcp-adapter-default-server 暴露到了公网。这条 REST 端点接受任何带 Application Password 的请求,包括来自 bot、扫描器、以及 prompt injection 攻击者的请求。

如果你不打算给整个站点加一道 Cloudflare Zero Trust,mcp-adapter 就是一个被精心包装的、可被批量枚举的 REST API。

这篇接 6/17 的「Cloudflare Tunnel 零端口方案」和 6/30 的「WordPress 7.0 MCP 集成实战」,是「AI 集成层」的安全收尾篇。我会讲清楚 6 个真实陷阱,以及如何用 Cloudflare Tunnel + Zero Trust Service Auth + mcp-adapter 自带的能力白名单,把 MCP 端点压缩到「只有我本机的 Claude Code 才调得动」。

架构预览:从 mcp-adapter 默认暴露到 Zero Trust 后的零信任通道

默认部署下,mcp-adapter 走 WordPress 内置 REST API 路由:

/wp-json/mcp/mcp-adapter-default-server

这个端点的鉴权靠 WordPress Application Password。任何拿到用户名 + Application Password 的人(包括 bot)都可以调用你注册的所有 Abilities,比如 site-audit/find-broken-linkscontent/bulk-update-meta

接入 Cloudflare Tunnel 之后,流量走向变成:

Claude Code (本机) → Cloudflare Edge (Zero Trust 自助登录) → cloudflared (内网回环) → 127.0.0.1/wp-json/mcp

关键的改变是:

1. **公网没有任何 TCP 端口在监听**——cloudflared 是 outbound-only 连接

2. HTTP 层先过 Zero Trust——没带 Service Token / 邮箱 OTP 的请求在 Cloudflare 边缘就被 403 掉

3. REST 层再有 Application Password 鉴权——双层认证

4. mcp-adapter 0.5.0 的能力白名单——只把必要的 Abilities 暴露给 default server

🛠️ 前置准备

在开始之前确认你的环境:

验证命令:

# 验证 mcp-adapter 安装
wp plugin list --allow-root | grep mcp-adapter
# 输出:mcp-adapter   0.5.0   active   ...

# 验证 Abilities API 注册
wp eval 'if ( function_exists( "wp_register_ability" ) ) { echo "OK"; } else { echo "NEED 6.9+"; }' --allow-root

# 验证 cloudflared 已在运行
ps aux | grep -E "[c]loudflared"
# 输出至少一行包含 tunnel

🚀 核心部署步骤

Step 1:把 mcp-adapter 默认 server 限定到只读 Abilities

mcp-adapter 0.5.0 引入的 mcp_adapter_server_config filter 可以裁剪 default server 暴露的能力集(PR #217 2026-06-17 合并)。

在自定义 plugin 或 mu-plugin 里加:

激活这个 mu-plugin,然后验证:

curl -s -u "your_user:your_app_password" \
  "https://your-domain.com/wp-json/mcp/mcp-adapter-default-server" \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' | jq '.result.tools[].name'
# 预期输出应该只包含 site-audit/* 与 content/read-* 命名的 tool

如果出现 content/bulk-update-meta 这类写操作,filter 没生效——99% 是 mu-plugin 加载顺序问题,把它移到 /wp-content/mu-plugins/ 而不是 theme 的 functions.php

Step 2:把 mcp.example.com 子域名接入现有 Tunnel

如果你已经在 6/17 的方案里跑着 cloudflared,不要为 MCP 单独开新 tunnel,加一条路由就行:

# ~/.cloudflared/config.yml(追加,不要覆盖)
tunnel: 
credentials-file: /root/.cloudflared/.json

ingress:
  # 已有规则保持原样...
  - hostname: mcp.example.com
    service: http_status:404
    originRequest:
      noTLSVerify: true
  - service: http_status:404

然后在 Cloudflare Zero Trust 控制台加 public hostname:

1. Zero Trust → Networks → Tunnels → 选你的 tunnel → Configure → Public Hostname

2. Subdomain: mcp

3. Domain: example.com

4. Service Type: HTTP

5. URL: localhost

注意我先写的是 http_status:404 占位——下一步要加 Zero Trust policy,没加之前不能直接转发到 wp-json。

重启 cloudflared:

sudo systemctl restart cloudflared
sudo systemctl status cloudflared

Step 3:给 mcp.example.com 加 Zero Trust Self-Hosted Application

在 Zero Trust 控制台:

1. Access → Applications → Add an application → Self-hosted

2. Name: WordPress MCP (Personal)

3. Session duration: 24 hours(不要选 Always,Service Token 会失效)

4. Application domain: mcp.example.com

5. Identity providers: 选 One-time PIN(邮箱验证码)

接下来加两条 Policy:

Policy 1 - 我的 Claude Code 走 Service Auth

Name:  Local Claude Code
Action: Allow
Session duration: 24 hours
Apply to: Service Auth - <你创建的 Service Token 名字>

Policy 2 - 浏览器手动调试走 OTP

Name:  Browser Debug
Action: Allow
Session duration: 1 hour
Apply to: Emails - your-email@example.com

Order 必须是 Service Auth 在前(更具体)。这两条之外的请求全部 Bypass 拒绝。

Step 4:把 Service Token 嵌入 Claude Code MCP 配置

Cloudflare Service Token 是用 Client ID + Client Secret 鉴权的长期凭证(不是 JWT,可以吊销)。

生成:Access → Service Auth → Create Service Token → 名字叫 local-claude-code → 拿到 Client ID 和 Client Secret。

在 Claude Code 的 MCP 配置里:

{
  "mcpServers": {
    "wordpress-mcp": {
      "url": "https://mcp.example.com/wp-json/mcp/mcp-adapter-default-server",
      "headers": {
        "CF-Access-Client-Id": "",
        "CF-Access-Client-Secret": "",
        "Authorization": "Basic " + base64("your_wp_user:your_app_password")
      }
    }
  }
}

或者用 claude mcp add-json

claude mcp add-json wordpress-mcp '{
  "url": "https://mcp.example.com/wp-json/mcp/mcp-adapter-default-server",
  "headers": {
    "CF-Access-Client-Id": "xxx.service-token",
    "CF-Access-Client-Secret": "xxx",
    "Authorization": "Basic '"$(echo -n 'your_user:your_app_password' | base64)""'"
  }
}'

验证:

claude mcp list
# 预期输出 wordpress-mcp: https://mcp.example.com/wp-json/mcp/mcp-adapter-default-server  (14 tools)

Step 5:开启 mcp-adapter observability 写审计日志

v0.5.0 的 observability handler 会在 tools/call 之后回调 record_event(),把它接进 WordPress 自带的 debug log:

add_action( 'mcp_adapter_observability_event', function( $handler, $event, $payload ) {
    if ( 'mcp.request' !== $event ) {
        return;
    }
    if ( 'success' !== ( $payload['status'] ?? 'unknown' ) ) {
        error_log( sprintf(
            '[MCP] %s user=%s tool=%s',
            $payload['status'],
            $payload['user'] ?? 'anon',
            $payload['method'] ?? 'unknown'
        ) );
    }
}, 10, 3 );

写入 /var/log/nginx/wordpress-access.log 或 wp-content/debug.log(logrotate 7 天保留就行)。

💣 踩坑录:6 个真实生产陷阱

陷阱 1:mcp-adapter 默认监听 wp-admin 下所有 REST 路由,能力全暴露

**症状**:刚装完 mcp-adapter,tools/list 返回所有已注册的 Abilities,包括 plugin/installuser/create 这种 destructive 操作的。

**根因**:default server 默认 include 所有已注册的 Abilities,0.5.0 才引入 mcp_adapter_server_config filter。

**修复**:必须装 Step 1 那个 mu-plugin。如果你跳过了这一步,mcp-adapter 就是一个被精心包装的、可被批量枚举的 REST API——任何能拿到 Application Password 的人(包括 prompt injection 攻击者)都能调 plugin/install 给你的站装后门。

陷阱 2:Application Password 配错导致 /wp-json/mcp 401 风暴

**症状**:nginx access log 大量 401,Claude Code 反复重试,mcp-adapter observability 里全是 auth_failed

**根因**:WordPress 6.9+ 引入了 application_password_is_api_request filter,要求 Application Password 请求必须命中特定 REST 路径,并且 HTTP Basic Auth 的用户名必须用 user_login 而不是 user_email

修复

// 不要用 email,用 login
$auth = 'Basic ' . base64_encode( 'my_wp_login:abcd EFGH ijkl MNOP qrst UVWX' );
// 注意 Application Password 里的空格要保留

WordPress 6.9 以下用 email 没事,但 7.0+ 会 401。验证命令:

curl -s -o /dev/null -w "%{http_code}" -u "your_login:your_app_pwd" \
  "https://your-domain.com/wp-json/mcp/mcp-adapter-default-server" \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
# 必须返回 200,返回 401 就是用户名格式问题

陷阱 3:Cloudflare Tunnel 反代到 127.0.0.1,但 wp-json 走 https:// 导致 cloudflared 526 错误

**症状**:第一次部署完,https://mcp.example.com/wp-json/mcp/... 报 526 Invalid SSL Certificate。

**根因**:cloudflared 转发到 http://localhost 是对的,但你的 nginx 监听着 443 而且只接受 example.com 的 SNI,mcp.example.com 的请求到了 nginx 那里就 526。

修复:两条路任选一条:

方案 A(推荐):用 Cloudflare Origin Certificate

1. Cloudflare 控制台 → SSL/TLS → Origin Server → Create Certificate

2. 覆盖 *.example.comexample.com

3. 把证书和私钥装到 nginx,对 mcp.example.com 也启用 HTTPS

4. cloudflared 改转发 https://localhost:443(保留 noTLSVerify: true)

方案 B:cloudflared 配 noTLSVerify + 强制 HTTP

ingress:
  - hostname: mcp.example.com
    service: http://127.0.0.1:80
    originRequest:
      noTLSVerify: true
      httpHostHeader: mcp.example.com

并在 nginx 给 mcp.example.com 配 80 端口的 vhost,不要走 443。

陷阱 4:Zero Trust policy 顺序错,Service Auth 被 OTP 抢走

**症状**:Service Token 的请求被 Cloudflare 弹 OTP 登录页(/cdn-cgi/access/login),Claude Code 收到 HTML 而不是 JSON,解析失败。

根因:Access policy 按顺序匹配,你的 OTP policy 在 Service Auth policy 之前。Cloudflare 默认「first match wins」,邮箱地址比 Service Auth 名字更宽泛。

修复:把 Service Auth policy 拖到最上面。验证顺序:

# 拿 Service Token 直接打
curl -s -H "CF-Access-Client-Id: xxx" -H "CF-Access-Client-Secret: yyy" \
  "https://mcp.example.com/wp-json/mcp/mcp-adapter-default-server" \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
# 必须直接拿到 JSON,不能出现 302 重定向到 login 页

如果仍然 302,policy 顺序错了。或者你的 Service Token 名字是 default——Cloudflare 不允许用 default 这种保留字作 service token 名字。

陷阱 5:Abilities API 检测不到,mcp-adapter 启动静默失败

**症状**:装完 mcp-adapter 后 tools/list 返回空数组,但 mcp-adapter-default-server 已注册。WordPress 6.9+ 站点,且 wp_register_ability 函数存在,但 wp_get_abilities() 返回 []

**根因**:Abilities 注册 hook 时机问题。多数 plugin 在 init hook 里注册,但 mcp-adapter 在 plugins_loaded 就开始抓了——注册的时机晚于 mcp-adapter 抓取时机。

修复:把 Abilities 注册 hook 提前:

// 不要用 init
add_action( 'plugins_loaded', function() {
    wp_register_ability( 'site-audit/find-broken-links', [...] );
}, 5 ); // 优先级 5,比默认 10 早

mcp-adapter 0.5.0 的 PR #196 已经在 default server 里加了 mcp_adapter_default_server_abilities filter,紧急时可以直接在 mu-plugin 里手动喂 Abilities 列表:

add_filter( 'mcp_adapter_default_server_abilities', function( $ids ) {
    return [
        'site-audit/find-broken-links',
        'content/read-meta',
        'content/read-taxonomies',
    ];
} );

陷阱 6:cloudflared 旧版(2024.x)不支持 streamable HTTP,mcp-adapter 0.5.0 的 streamable transport 用不了

**症状**:本地 Claude Code 走 streamable HTTP 调 mcp-adapter,cloudflared 日志大量 400 Bad Request: invalid HTTP version,但 SSE 模式能工作。

根因:cloudflared 2024.x 还在用 HTTP/1.1 中转,2025 年初的版本才支持 HTTP/2 stream。mcp-adapter 0.5.0 的 streamable transport 默认走 HTTP/2。

修复

cloudflared --version
# 2025.4.0 或更新

如果你的发行版仓库里 cloudflared 太旧(Ubuntu 22.04 默认 2024.8.0),用 Cloudflare 官方 apt 源:

curl -fsSL https://pkg.cloudflare.com/cloudflare-main.gpg \
  | sudo tee /usr/share/keyrings/cloudflare-main.gpg >/dev/null
echo "deb [signed-by=/usr/share/keyrings/cloudflare-main.gpg] https://pkg.cloudflare.com/cloudflared $(lsb_release -cs) main" \
  | sudo tee /etc/apt/sources.list.d/cloudflared.list
sudo apt update && sudo apt install cloudflared

🛡️ 进阶配置:让 mcp-adapter 真的只属于你

完成 Step 1-5 之后,剩下还有三件事可以加固:

1. 给 default server 加 IP 白名单(双重保险)

Cloudflare Tunnel 的回环 IP 是 127.0.0.1,但你可能其他服务也跑在同台机器。给 mcp.example.com 的 nginx vhost 加个内网白名单:

location /wp-json/mcp/ {
    allow 127.0.0.1;
    allow ::1;
    deny all;

    try_files $uri $uri/ /index.php?$args;
}

这样即使有人绕过 Cloudflare 直接打 nginx,公网也调不到。

2. rate-limit mcp-adapter 端点

mcp-adapter 走 WordPress REST API,但你可以在 nginx 给 /wp-json/mcp/ 加独立 limit_req:

limit_req_zone $binary_remote_addr zone=mcp_limit:10m rate=10r/m;

location /wp-json/mcp/ {
    limit_req zone=mcp_limit burst=20 nodelay;
    try_files $uri $uri/ /index.php?$args;
}

10 r/m 足够 Claude Code 日常调用,burst=20 防止误触发。

3. mcp-adapter 0.5.0 的 transport permissions 锁死方法集

0.5.0 的 mcp_adapter_server_config filter 支持 allowed_methods

$config['allowed_methods'] = [ 'tools/list', 'tools/call' ];
// 不要给 'resources/read','prompts/get' 这些 mcp-adapter 当前不需要的 method

减少 attack surface。

总结

6/17 写了 Cloudflare Tunnel 让你的站点公网零端口,6/30 写了 mcp-adapter 让 AI 直接读写站点,这篇是两者之间的安全桥梁:mcp-adapter 默认端点必须经过 Zero Trust 鉴权,Abilities 必须白名单,cloudflared 必须新版,observability 必须开日志。

完整 checklist:

  • [ ] Step 1:`mcp_adapter_server_config` filter 限定 read-only Abilities
  • [ ] Step 2:cloudflared ingress 加 `mcp.example.com`
  • [ ] Step 3:Zero Trust Self-hosted App + Service Auth policy
  • [ ] Step 4:Service Token 嵌入 Claude Code MCP headers
  • [ ] Step 5:observability handler 写错误日志到 debug.log
  • [ ] 进阶:nginx IP 白名单 + rate-limit + transport method 锁定

mcp-adapter 0.5.0(2026-06 release)的 observability 子系统是这套架构的关键,之前版本没有 mcp_adapter_observability_event hook,所有 MCP 调用都是黑盒。

下一篇方向:WordPress 7.0 协作编辑实战(HTTP polling sync provider 与多人同时编辑冲突解决),或者 mcp-adapter 0.5.0 完整 benchmark(不同 Abilities 的延迟对比)。

👉 Join MiniMax Token Plan: AI coding acceleration for businesses

👉 Join Zhipu Coding Plan: GLM-4.6/GLM-5 coding packages, China-stable, pay-per-token unlimited

👉 Join Aliyun AI: Top AI products with exclusive coupons for business innovation

📌 This article was AI-assisted generated and human-reviewed | TechPassive — An AI-driven content testing site focused on real tool reviews

🔗 Recommended Tools

These are carefully selected tools. Using our affiliate links supports us to keep producing quality content:

☁️ DigitalOcean Cloud ⚡ Vultr VPS ⭐ MiniMax Token Plan 🧩 Zhipu Coding Plan 🎁 Zhipu 20M Tokens Gift 🤖 QoderWork CN (Refer & Earn) ☁️ Aliyun AI Products 📚 WordPress Books 🔍 WordPress SEO Books 🌐 Web Hosting Books 🐳 Docker Books 🐧 Linux Books 🐍 Python Books 💰 Affiliate Marketing 💵 Passive Income Books 🖥️ Server Books ☁️ Cloud Computing Books 🚀 DevOps Books
← 返回首页