打造一個安全的 DynamoDB MCP server 需要付出什麼
讓你可以輕而易舉地把資料庫交給 。這正是問題所在。大多數 DynamoDB MCP server 都只是一個薄薄的程序, 握著你的原始 AWS 憑證,帶著一條寫入路徑,卻沒有任何審查步驟 — 我們已經從使用者的角度寫過為什麼這種模式有風險、以及如何安全地做選擇。
這篇文章談的是另一面:實際打造出安全版本究竟需要付出什麼。 DynoTable 是一個桌面 DynamoDB 用戶端,它的 MCP server 會把受控管的 AI 工具組公開給外部 agent — Claude Code、Cursor、Codex — 而 agent 完全不會碰到 AWS 憑證。要做到這一點, 意味著要在 Electron 應用程式_內部_推出一個 授權伺服器、 設計一種讓憑證混用變得無法表述的識別型別,並且不只一次地判定:那個方便的預設值才是錯的選擇。
如果你正圍繞任何敏感的東西建置 MCP server,這些決策你同樣也會遇到。
Loopback HTTP 不會自動就是安全的
伺服器綁定 127.0.0.1,這聽起來安全,其實還不夠。你造訪的任何網頁都可能嘗試向
localhost 發出請求,而 DNS 重新綁定(rebinding)則讓惡意網頁能替這些請求套上一個看似合理的
Host。因此第一層防護是一道 origin 守門:拒絕任何帶有真實網頁 Origin 的請求、
拒絕非 loopback 的 Host 標頭 — 但允許完全沒有 Origin 的請求,
因為像 Claude Code 這類正當的 CLI 用戶端本來就不會送 Origin。
最後那一條是誠實的部分:origin 守門只能擋下瀏覽器這類攻擊。它無法驗證任何人的身分。 真正的關卡是 bearer token,而這帶出了一個問題:token 從何而來。
我們在應用程式內推出了一個 OAuth 2.1 授權伺服器
MCP 的遠端伺服器驗證方案就是 OAuth 2.1,而對於本機桌面伺服器來說, 並沒有上游的身分提供者可以倚靠。所以應用程式本身_就是_授權伺服器: 動態用戶端註冊、(S256)、授權碼、refresh token、撤銷, 以及讓用戶端能夠探索這一切的 RFC 8414 / RFC 9728 中繼資料文件。
我們很早就定下並始終遵守的規則:
- 別自己手刻協定。 MCP SDK 以 router 的形式提供了完整的授權伺服器介面; 我們只實作其底下的儲存/政策 provider。我們接受的一個後果是: 我們的 MCP 框架內部使用一套 HTTP 堆疊,而 SDK router 使用另一套, 所以授權伺服器會以第二個 loopback 監聽器的形式執行, 由資源伺服器透過受保護資源的中繼資料把用戶端導向它。 兩個監聽器是一項有明確範圍、可掌控的成本; 而手工重新推導 PKCE 與 token 文法則是一項沒有邊界的成本。
- 重新導向只限 loopback(依原生應用程式的 RFC 8252), 而無效的重新導向永遠不會拿到錯誤導向 — 它會直接拿到 400, 所以攻擊者的 URI 永遠不會成為目的地。
- 授權碼一次性有效 — 被重放的授權碼就是無效的 grant,沒有例外。
- Refresh 可以縮小 scope 但永遠不能放大它,而且它會逐字重新簽發同一份識別內容 — refresh 過的 token 無法悄悄取得另一個區域。
- 每個請求都會重新檢查撤銷狀態,而非在連線時快取 — 在設定中點下「撤銷」,就會扼殺該用戶端的下一次呼叫。
開發建置版本則改用一條更簡單的驗證路徑,其建構方式使它不可能存在於釋出版二進位檔中 — 在出貨的軟體中放一個未經驗證、擁有完整 scope 的 token, 等於在一道刻意放行無 Origin 請求的 origin 守門之後留了一個本機後門。
一個連線,一個 AWS 識別 — 完整攜帶,絕不臆測
核心的安全特性是:外部 agent 連上的是_某個設定檔_,而非「DynoTable」。 每個連線都綁定到單一 AWS 設定檔與單一區域,背後是它自己專屬、不共用的 DynamoDB 用戶端, 所以兩個位於不同設定檔的 agent 無法彼此滲透。
這個綁定一開始是一組鬆散的欄位 — awsProfile、region、profileId、
一個可選的本機連接埠 — 在這條路徑上的每一筆記錄裡重複宣告。
如今它是一個帶有兩個分支的可辨識聯合(discriminated-union)識別型別:
online(設定檔 + 區域 + id)與 offline(本機連接埠 + id,
供 DynamoDB Local 設定檔使用)。是否離線本身就是判別依據,
所以一個沒有憑證的 online 識別、或一個帶著區域的 offline 識別,
都是_型別錯誤_,而不是執行期才冒出的意外。這個單一型別完整無缺地穿越
同意 → token → grant → 每一個請求。
有兩個後果我們視為關鍵:
- 區域在同意時被凍結。 使用者核准的是「區域 Y 中的設定檔 X」,
而該組合會被釘在 token 上 — 撐過 refresh,永遠不會在熱路徑上從可變狀態重新解析。
要編輯設定檔的區域就必須重新同意。另一種做法 — 每個請求重新解析區域、
或退回到某個預設值 — 正是 agent 悄悄跑去查詢
us-east-1的原因。 - 格式錯誤的 token 一律是 401,絕不強轉。 如果一個 token 的識別聲明無法解析成一個有效的分支,
該請求就是未授權。沒有
'default'設定檔、沒有預設區域、沒有空字串。 舊程式碼會靠臆測帶過的每一個地方,如今都是硬性失敗。
同意是一個產品介面,而不是一個對話框
第一版的同意提示是平台原生的訊息框。它在開啟時會凍結 Electron 主程序、 無法比對應用程式的設計系統,甚至無法為了文件而截圖。如今它是一個應用程式內的 modal: 連線用戶端的名稱、一個涵蓋使用者確實選擇公開之設定檔的設定檔選擇器, 以及依由窄到寬順序提供的三種 scope — read-only、read + stage、full。 過期或重複的同意請求會被結算為拒絕,而不會卡住 OAuth 流程。

選擇器本身有一個小小的信任邊界:用戶端可以在 authorize URL 中_提示_一個設定檔, 但 grant 是依使用者實際選取的內容鑄造,並會對照他們所看到的快照進行驗證 — 一個偽造的提示無法綁定一個從未出現在畫面上的設定檔。
我們同樣明確地說明同意不會給你什麼:授權只涉及 scope,且每個連線只核准一次。 應用程式內助理的每次呼叫權限提示並不適用於 MCP 流量 — 一個外洩的完整 scope token 可以寫入(進入暫存區),而不必讓人看到每一次呼叫。 短暫的 token 生命週期、每個用戶端各自的撤銷、一份始終啟用、標記每一個源自 MCP 之動作的稽核軌跡, 以及以 read-only 作為預設提供的選項,就是這些的緩解措施。 把這件事寫下來當作殘餘風險,比假裝同意 modal 已經把它關掉來得有用。
為無介面工作設下的護欄
外部 agent 沒有 renderer、沒有確認彈窗、沒有使用者在旁盯著。 每一項互動式的東西都需要一個無介面的等價物:
- 每次呼叫都做即時的授權夾制。 grant 儲存的是使用者核准的 scope; _有效_的 scope 則在每次工具呼叫時,依應用程式當下的授權狀態推導出來 — read-only 授權會把一個完整 grant 夾制下來、 已登出狀態會在任何 token 分支之前就以 401 拒絕(所以開發用 token 也無法繞過它), 而工作階段中途的重新啟用則會在不必重新同意的情況下重新放大。 有一個刻意的不對稱:在 read-only 授權下,應用程式內匯出仍然可用(你的資料就是你的資料), 但透過 MCP 的無介面大量匯出則被夾制關閉 — 互動式的可攜性權利與 agent 驅動的外流管道是不同的信任面。
- 以掃描預算取代確認彈窗。 在應用程式裡,全表讀取會先詢問。 在無介面的情境下,一個以項目數/位元組數/時長計的預算會約束每一次掃描, 而針對表格的 SQL 則透過一個以磁碟為基底的引擎串流,而非某個記憶體上限。
- 斷線會誠實地中止。 如果用戶端在掃描中途斷線, 伺服器會在下一個分頁邊界中止 — 絕不會在語句中途中止 — 並釋放暫時狀態。
- 可以透過連線完成。 當 AWS 憑證需要一組 MFA 驗證碼、 而用戶端支援 MCP elicitation 時,伺服器會_透過 agent 自己的 UI_ 徵求那組六位數驗證碼 — Claude Code 會向使用者提示 — 然後重試。SSO 被刻意排除在這條路徑之外: 它需要一個瀏覽器裝置流程,而假裝並非如此只會變成一個更糟的錯誤訊息。 需要相同憑證的並行呼叫會合併成單一次提示。
透過 MCP 公開資料庫究竟安不安全?
我們誠實的答案是:唯有當伺服器的設計,是圍繞著「agent 往好裡想是搞不清楚狀況、 往壞裡想是有敵意」這個假設時,它才安全。 讓 agent 的_意圖_變得不可信, 這正是為什麼整個架構從不依賴 agent 會乖乖行事:憑證留在應用程式裡、 寫入永遠只落在一個可審查的暫存區、 scope 會夾制一次呼叫能做什麼,而不論模型要求什麼,而且每一個動作都會被稽核。 連線端指南涵蓋了這對每個用戶端在實務上的意義。
對任何會碰到你資料庫的 MCP server,你該提出什麼要求
- 憑證存放在哪裡 — 在 agent 的程序裡,還是在伺服器背後?
- 是否有帶著真實 scope 的、每個連線各自的同意,還是一個設定檔就授予一切?
- 寫入是否可以在沒有人工審查步驟的情況下執行?
- 識別(設定檔、區域)是在同意時被釘住,還是在呼叫時從預設值解析而來?
- 面對格式錯誤或已撤銷的 token 會發生什麼 — 是硬性失敗,還是退回某個備援?
- 是否有一份能區分出源自 agent 之動作的稽核軌跡?
- 什麼在約束一場失控的掃描,而當用戶端在操作中途斷線時又會發生什麼?
如果一個伺服器答不出這些,那個方便的整合就是有風險的那個。 若要衡量一場無邊界的掃描實際上會花你多少錢, 定價計算器會讓你清醒 — 而如果你寧願親眼看到安全版本在跑,而不是讀關於它的文字, 就下載 DynoTable、打開 MCP server,在不到一分鐘內以 read-only scope 連上一個用戶端。


