實戰背景
Claude-Mem 把記憶落在本地 SQLite,比起雲端日誌更安全,但「本地」不等於「可以亂寫」。最常見的意外是:除錯過程中,AI 把一段包含範例金鑰或內部 URL 的程式碼當成「重要觀察」存了下來。一旦這台機器被其他人接手、或記憶檔被備份上傳,就會變成資安破口。
痛點與問題描述: 我們在排查 Stripe webhook 簽章驗證時,AI 為了方便日後回顧,把一段「測試用的範例簽章 + webhook secret」一起寫進了觀察。範例 secret 雖然不是正式金鑰,但格式真實,被當成真金鑰濫用的風險仍在。我們需要:
- 用
get_observations找出含敏感內容的觀察 ID。 - 用
forget從本地 SQLite 徹底刪除。 - 用
<private>標籤與 v13.13.0 起的sensitive分類,從源頭預防再次寫入。
本範例走完敏感記憶的「發現 → 擦除 → 預防」三步。
端到端 6 輪協作紀錄
── 第 1 輪:察覺異常 ──
-
You:
我剛剛在 Web Viewer 串流頁看到一條觀察,內容好像有 webhook secret。 幫我用 get_observations 查 stripe 或 webhook 相關的記憶。 -
AI:
我來檢索可能含敏感內容的條目。
CM 呼叫:
get_observations(query: "stripe webhook secret sign")CM 回傳:[ { "observationId": "obs_2078", "category": "debugging", "summary": "Stripe webhook 簽章驗證:使用 whsec_abc123EXAMPLEsecret 作為測試金鑰,於 webhookHandler.ts 第 28 行驗證 sig header。" } ]確認問題:
obs_2078含有 webhook secret 格式的字串whsec_abc123EXAMPLEsecret。雖然標了 EXAMPLE,但格式是真實的 Stripe secret,不該留在記憶庫。
── 第 2 輪:擴大搜尋,確認沒有漏網之魚 ──
-
You:
再用 key、token、password、secret 幾個關鍵字各查一次,別只看 obs_2078。 -
AI:
合理,敏感字串可能散在多條觀察。我並行查四個關鍵字。
CM 呼叫(四次):
get_observations(query: "api key") get_observations(query: "bearer token") get_observations(query: "password") get_observations(query: "secret")CM 回傳彙整:
[ {"observationId": "obs_2078", "summary": "...whsec_abc123EXAMPLEsecret..."}, {"observationId": "obs_2103", "summary": "資料庫連線字串使用 ${DB_PASSWORD} 環境變數,未硬編碼。"} ]結果:
obs_2078必須刪除;obs_2103提到DB_PASSWORD但只是說明使用環境變數,沒有實際值,保留。這一步很重要:
forget不該無腦全刪,必須逐筆判讀,避免把「提到金鑰規範」的安全守則跟「真的含金鑰」的髒資料一起清掉。
── 第 3 輪:執行 forget 安全擦除 ──
-
You:
好,刪掉 obs_2078。 -
AI:
我來執行擦除。
CM 呼叫:
forget(observationId: "obs_2078")CM 回傳:{"status": "deleted", "observationId": "obs_2078"}obs_2078已從本地 SQLite 徹底移除。再次查詢確認:CM 呼叫:
get_observations(query: "whsec")CM 回傳:[]空陣列,敏感記憶已清乾淨。
── 第 4 輪:用 <private> 標籤預防未來寫入 ──
-
You:
下次除錯時要怎麼避免 AI 又把 secret 存進去? -
AI:
Claude-Mem 支援
<private>標籤。在對話中,把不想被寫入記憶的內容用<private>...</private>包起來,自動壓縮與顯式save_observation都會跳過這段。實務做法是:貼上真實金鑰、JWT、連線字串時,用標籤包住:
我們的 webhook 驗證邏輯如下,使用這個 secret: <private>whsec_realSecretValueHereDoNotLog</private> 請幫我檢查簽章驗證流程。這段
<private>內容會出現在當下對話讓我能讀取處理,但不會被壓縮進持久化記憶。get_observations與timeline之後都查不到。
── 第 5 輪:套用 sensitive 分類(v13.13.0+) ──
-
You:
那如果是「不是秘密、但不宜外流」的內容呢?例如內部 staging URL、未發布的營收數字。 -
AI:
v13.13.0 起新增了第 9 類觀察分類
sensitive,專門處理這種「非隱私、但不宜外流」的內容:內部 URL、未發布計畫、商業指標。寫入時直接指定:CM 呼叫:
save_observation( key: "staging_endpoint_internal", value: "內部 staging 環境位於 staging-internal.example.net,僅 VPN 可達,禁止寫入對外文件。", category: "sensitive" )CM 回傳:
{"status": "saved", "observationId": "obs_2110"}重點:
sensitive類別在預設安裝下會自動觸發 Telegram 通知,提醒有人寫入了一條需要留意的內容。既有安裝會在升級到 v13.13.0 時,把舊的security_alert自動遷移成security_alert,sensitive,自訂過的清單則保留不動。如果你不想接 Telegram 通知,可在設定檔調整會觸發通知的分類清單(theme.yaml 中提到的
security_alert,sensitive即為此清單格式),但不建議把sensitive整個關掉,這條分類是資料外流的早期預警。
── 第 6 輪:建立寫入前自檢 SOP ──
-
You:
幫我定義一個檢查規則,讓 AI 每次寫觀察前都先過一次。 -
AI:
建議把這條 SOP 本身寫成一條
architecture類別的觀察,這樣未來所有 Session 都會讀到:CM 呼叫:
save_observation( key: "observation_write_precheck", value: "呼叫 save_observation 前必須自檢:(1) value 中不得含 whsec_/sk_/Bearer/密碼格式字串;(2) 真實金鑰須以 <private> 包住;(3) 內部 URL、未公開指標改用 sensitive 分類;(4) 不確定時先問使用者再寫入。", category: "architecture" )CM 回傳:
{"status": "saved", "observationId": "obs_2111"}從此以後,我在每次呼叫
save_observation之前都會先對照obs_2111的四點檢查表,把防線推到寫入之前,而不是等髒資料進庫再用forget收拾。
關鍵學習點
- forget 之前先逐筆判讀:用多個關鍵字(key、token、password、secret)擴大搜尋,但要人工判斷哪些是「含真實金鑰」、哪些只是「提到金鑰規範」,後者必須保留,否則會連安全守則一起清掉。
<private>標籤是源頭防線:在對話中貼真實金鑰、JWT、連線字串時用<private>...</private>包住,內容當下可讀但不會被壓縮進持久化記憶,比事後forget更省事。- sensitive 分類不等於機密:v13.13.0 起的
sensitive類別處理的是「非隱私但不宜外流」的內容(內部 URL、未發布計畫、商業指標),預設觸發 Telegram 通知,是把外流風險提前預警的機制。 - 把檢查規則本身寫進記憶:把「寫入前自檢 SOP」存成一條 architecture 觀察,防線就從事後擦除推進到事前攔截,這是 Claude-Mem 自我演進的典型用法。