搭配嚴格管制措施:IP 白名單、最小角色、pending 狀態、Discord 通知。攻擊面最小、效率最高、可追溯。
| 方案 | 效率提升 | 實作複雜度 | 整體風險 | 適合此場景 |
|---|---|---|---|---|
| WordPress REST API + Application Passwords | 高 | 低 | 中 | ✓ 推薦 |
| 自訂 REST Endpoint(限定存取範圍) | 高 | 中 | 低 | ✓ 最推薦 |
| Elementor MCP Plugin(bvisible/elementor-mcp-api) | 極高 | 低 | 中 | ✓ 值得評估 |
| SSH/SFTP 直接改檔案 | 中 | 低 | 高 | ✗ 不推薦 |
| WP-CLI 遠端執行 | 中 | 中 | 高 | ✗ 不推薦 |
| 第三方外掛(WPCode 等) | 低 | 低 | 低 | △ 功能有限 |
Authorization: Basic base64(user:app_password))呼叫 /wp-json/wp/v2/pages/{id}content 欄位(包含 Elementor 的 _elementor_data postmeta)| 風險項目 | 等級 | 說明 |
|---|---|---|
| Token 洩漏 | 高 | Application Password 被截取,攻擊者可用對應帳號全部權限操作 |
| 預設繞過 2FA | 中 | Application Passwords 本來就設計成繞過登入流程,不經過 2FA 驗證 |
| 用戶枚舉 | 中 | /wp-json/wp/v2/users 預設公開,洩漏帳號資訊 |
| Elementor data 損壞 | 中 | 直接寫 _elementor_data JSON 格式若有誤,會導致頁面 Elementor 資料損毀 |
| 低權限帳號建立 App Password | 中 | 預設所有角色(含 Subscriber)都能建立 App Password |
管理員帳號:攻擊者取得全站控制權,可安裝惡意外掛、建立後門帳號、竄改所有內容。
Editor 帳號:攻擊者可修改 / 刪除所有文章頁面,但無法更動外掛、主題、系統設定。
直接更新 post_content 可能觸發 Elementor CSS 重新快取需求
若 Elementor JSON 格式錯誤,頁面會顯示為純文字而非設計稿
不影響 SEO 結構本身,但若不刷新快取,CDN 可能繼續服務舊版本
// 自訂 must-use plugin(wp-content/mu-plugins/ai-elementor-access.php)
register_rest_route('ai-kunhang/v1', '/update-widget', [
'methods' => 'POST',
'callback' => 'ai_update_elementor_widget',
'permission_callback' => function() {
// 只允許特定自訂能力
return current_user_can('ai_edit_widgets');
},
'args' => [
'page_id' => ['required' => true, 'type' => 'integer'],
'widget_id' => ['required' => true, 'type' => 'string'], // Elementor widget ID
'html' => ['required' => true, 'type' => 'string'], // 新 HTML 內容
],
]);
| 風險項目 | 等級 | 說明 |
|---|---|---|
| Token 洩漏最壞情況 | 低 | 攻擊者只能改特定頁面的特定 widget,影響範圍極小 |
| Elementor data 損壞 | 低 | callback 內可做 JSON 驗證,格式錯誤直接拒絕 |
| 誤操作 | 低 | 可強制寫入前建立 revision |
| 維護成本 | 中 | 需要自己維護這個 mu-plugin |
| 外掛 | 功能 | 授權 | 備注 |
|---|---|---|---|
| bvisible/elementor-mcp-api | REST API + MCP 協議,Widget 級別操作 | GPL-3.0 | 最完整,可 PATCH 單一 widget |
| msrbuilds/elementor-mcp | 97 個 AI 工具,涵蓋 layout + widget | 未知 | 功能最豐富 |
| Royal MCP | MCP Server + OAuth 2.0 + 限速 + 日誌 | 商業 | 安全機制最完整 |
update_widget_content(page_id, widget_id, html) 等工具| 風險項目 | 等級 | 說明 |
|---|---|---|
| 外掛本身的漏洞 | 中 | 第三方外掛,需評估維護頻率和程式碼品質 |
| 平行操作覆蓋 | 中 | 多個 PATCH 請求並行會互相覆蓋(已知問題) |
| CSS 快取未刷新 | 低 | 需在每次更新後呼叫 /flush-css endpoint |
| Token 洩漏 | 中 | 使用 Application Passwords,風險同方案 1 |
特別注意:bvisible/elementor-mcp-api 的 Elementor widget ID 是 8 字元 hex,要先用 GET 取得頁面結構才能知道正確 ID。
| 風險項目 | 等級 | 說明 |
|---|---|---|
| SSH key 洩漏 | 極高 | 攻擊者取得整台伺服器控制權,不只是 WordPress |
| 誤刪系統檔案 | 高 | AI 操作失誤可能刪除核心 WordPress 檔案 |
| 沒有 WordPress 層的驗證 | 高 | 繞過所有 WP 的 permission 和 hook 機制 |
| Elementor data 損壞 | 高 | 直接修改 DB dump 或主題檔案極易出錯 |
| 難以審計 | 高 | SSH 操作難以追蹤到具體的 WordPress 層變更 |
Token 洩漏最壞情況:完整伺服器控制,資料庫可被 dump,可植入後門。
# 透過 SSH 遠端執行 WP-CLI
ssh user@kunhang.com.tw "wp post update 123 --post_content='<new content>'"
| 風險項目 | 等級 | 說明 |
|---|---|---|
| 需要 SSH 存取 | 高 | 同方案 4,SSH key 洩漏風險 |
| 可執行任意 PHP | 高 | wp eval 可執行任意 PHP 程式碼 |
| 審計能力 | 中 | WP-CLI 有 activity log 外掛可搭配 |
| 比純 SSH 受控 | 低(相對) | 操作在 WP 框架內,不能直接改伺服器系統檔 |
WPCode 主要用途是管理 code snippets(PHP/CSS/JS),沒有原生 REST API 讓外部系統操作。目前沒有成熟的 WPCode REST API 方案可讓 AI 透過 API 新增 / 修改 snippets。
評估:對 Elementor HTML Widget 的修改需求不適用此方案。
// 在 mu-plugin 或 functions.php 中建立專用角色
function create_ai_agent_role() {
add_role('ai_agent', 'AI Agent', [
'read' => true,
// 只給能修改頁面的最小能力
'edit_pages' => true,
'edit_published_pages' => true,
// 明確不給的能力
'publish_pages' => false, // 不能直接發佈
'install_plugins' => false,
'manage_options' => false,
'delete_pages' => false,
]);
// 加入自訂能力
$role = get_role('ai_agent');
$role->add_cap('ai_edit_widgets');
}
add_action('init', 'create_ai_agent_role');
關鍵原則:
• AI 帳號預設只能寫 draft,不能直接 publish
• 完全不給 manage_options(不能動外掛 / 主題設定)
• 完全不給 install_plugins / activate_plugins
// 在自訂 endpoint 中限制可操作的頁面
const ALLOWED_PAGE_IDS = [123, 456, 789]; // kunhang 網站的特定頁面 ID
function ai_update_elementor_widget(WP_REST_Request $request) {
$page_id = $request->get_param('page_id');
// 白名單檢查
if (!in_array($page_id, ALLOWED_PAGE_IDS)) {
return new WP_Error('forbidden', '此頁面不在允許修改清單中', ['status' => 403]);
}
// 建立修改前的 revision(備份)
wp_save_post_revision($page_id);
// 修改指定 widget 的 HTML
$elementor_data = get_post_meta($page_id, '_elementor_data', true);
$data = json_decode($elementor_data, true);
$widget_id = $request->get_param('widget_id');
$new_html = wp_kses_post($request->get_param('html')); // 清洗 HTML
// 遞迴找到 widget 並更新
$updated = update_widget_in_tree($data, $widget_id, $new_html);
if (!$updated) {
return new WP_Error('not_found', '找不到指定的 widget ID', ['status' => 404]);
}
// 儲存更新(狀態改為 pending review,不直接 publish)
update_post_meta($page_id, '_elementor_data', wp_slash(json_encode($data)));
wp_update_post(['ID' => $page_id, 'post_status' => 'pending']);
// 記錄操作日誌
ai_log_operation($page_id, $widget_id, get_current_user_id());
return ['success' => true, 'message' => '修改已儲存為待審核狀態'];
}
三層備份策略
| 層級 | 機制 | 恢復方式 | 保留期 |
|---|---|---|---|
| WordPress Revisions | 每次修改前自動建立 revision | 後台 → 頁面 → 版本 | 最近 25 個版本 |
| 外掛備份(Duplicator / UpdraftPlus) | 每日排程備份整個網站 | 外掛一鍵還原 | 30 天 |
| 主機快照(若有) | Hetzner / cPanel 層級快照 | 主機控制台還原 | 依主機商設定 |
Elementor 快取刷新(每次修改後必做):
POST /wp-json/neoservice/v1/flush-css
或透過 WP-CLI:
wp elementor flush-css
Cloudflare Dashboard → Security → WAF → Custom Rules:
(http.request.uri.path contains "/wp-json/ai-kunhang/")
AND
(not ip.src in {你的Hetzner伺服器IP/32 Claude雲端IP/32})
→ 動作:Block
<LocationMatch "^/wp-json/ai-kunhang/">
Order Deny,Allow
Deny from all
Allow from 195.x.x.x # Hetzner Claude 伺服器 IP
</LocationMatch>
add_filter('rest_authentication_errors', function($result) {
if (strpos($_SERVER['REQUEST_URI'], '/ai-kunhang/') !== false) {
$allowed_ips = ['195.x.x.x']; // Hetzner IP
$client_ip = $_SERVER['HTTP_CF_CONNECTING_IP'] ?? $_SERVER['REMOTE_ADDR'];
if (!in_array($client_ip, $allowed_ips)) {
return new WP_Error('ip_blocked', '不允許的 IP', ['status' => 403]);
}
}
return $result;
});
建議外掛:WP Activity Log(wp-security-audit-log)
• 記錄所有 REST API 修改
• 包含:操作時間、IP、使用者、修改內容摘要
• 可設定異常告警(短時間大量修改)
function ai_log_operation($page_id, $widget_id, $user_id) {
$log_entry = [
'time' => current_time('mysql'),
'user_id' => $user_id,
'page_id' => $page_id,
'widget_id' => $widget_id,
'ip' => $_SERVER['HTTP_CF_CONNECTING_IP'] ?? $_SERVER['REMOTE_ADDR'],
];
// 寫入自訂 table 或 option(短期快取)
$logs = get_option('ai_operation_logs', []);
array_unshift($logs, $log_entry);
$logs = array_slice($logs, 0, 100); // 保留最近 100 筆
update_option('ai_operation_logs', $logs);
}
工作流程設計:
AI 呼叫 API ↓ 修改內容寫入 _elementor_data(存為 pending 狀態) ↓ 發送通知給管理員(Email / Discord webhook) ↓ 管理員審核預覽(WordPress 後台 → 預覽) ↓ 確認 → 手動 Publish 或 拒絕 → 還原 Revision
function notify_admin_for_review($page_id, $widget_id) {
$preview_url = get_preview_post_link($page_id);
$message = "AI 已更新頁面 #{$page_id} 的 widget {$widget_id},請審核後發佈:{$preview_url}";
wp_remote_post(DISCORD_WEBHOOK_URL, [
'body' => json_encode(['content' => $message]),
'headers' => ['Content-Type' => 'application/json'],
]);
}
推薦方案:「自訂最小 REST Endpoint + MCP」雙軌架構
理由:①攻擊面最小——只暴露允許頁面的 widget 修改 ②效率最高——AI 直接呼叫,不需複製貼上 ③可追溯——每次操作都有 revision + 日誌 ④人工確認門——AI 改完存為 pending,人確認才發佈 ⑤Cloudflare IP 白名單——非 Hetzner 伺服器的請求直接 Block
| 威脅場景 | 發生機率 | 影響程度 | 緩解措施 |
|---|---|---|---|
| Application Password 洩漏 | 低–中 | 中(Editor 角色限制) | IP 白名單 + 最小角色 |
| Elementor JSON 格式錯誤 | 中 | 中(頁面顯示異常) | API 層 JSON 驗證 + Revision |
| AI 誤更新錯誤頁面 | 低 | 中 | 頁面 ID 白名單 |
| 外部攻擊者偽裝呼叫 | 低 | 低(IP 白名單阻擋) | Cloudflare WAF |
| 直接 publish 而不 pending | 低 | 中(跳過人工審核) | API 強制 pending 狀態 |
| Elementor CSS 快取未刷新 | 中 | 低(舊樣式短暫顯示) | API 呼叫完自動刷快取 |
/wp-json/ 路徑限制只允許特定 IP 存取(Hetzner Claude 伺服器 IP),立即降低外部攻擊風險。ai_agent 角色 + Application Password,開始用標準 REST API 測試 Elementor 頁面修改流程,確認 JSON 格式正確後再自動化。不要給 AI 帳號 SSH 存取或 Admin 角色。就算要方便,這兩個的風險邊界太寬,一旦洩漏就是全站(甚至全伺服器)淪陷。