Permissions、Row/Column Security 與 Sensitive Data
學習目標:傳遞使用者授權,資料最小揭露,阻擋未授權欄位與外洩路徑。
一位客服分析師問 agent「各實體的平均回應時間」,但 agent 的查詢工具因為底層查詢預設選取所有欄位,順便回傳了一欄他根本沒問過的聯絡方式資料。Data Agent 必須在每一次工具呼叫中帶著提問者實際的授權,而不是 agent 自己那個範圍寬鬆的服務帳號權限;也必須只回傳回答問題所需的最少資料,絕不是「整列資料先給你,也許用得到」。
ROLE_COLUMN_ACCESS = {
"support_analyst": {"run_id", "source_system", "batch_id", "metric", "value", "schema_version"},
"platform_admin": {"run_id", "source_system", "batch_id", "entity_id", "metric", "value", "schema_version"}
}
def query_metric_summary(user_role: str, source_system: str, metric: str) -> pd.DataFrame:
allowed_columns = ROLE_COLUMN_ACCESS.get(user_role)
if allowed_columns is None:
raise PermissionError(f"role not recognized, denying by default: {user_role}")
result = sandbox_view.query(source_system=source_system, metric=metric)
return result[[c for c in result.columns if c in allowed_columns]] # 欄位層級最小化| 層級 | 規則 |
|---|---|
| 身分 (Identity) | 每一次工具呼叫都帶著提問者的角色,而不是 agent 自己寬鬆的憑證 |
| Row security | 只篩選出該角色/部門有權看到的資料列 |
| Column security | 只回傳與問題相關的欄位,而不是底層資料表的所有欄位 |
| 外洩路徑 | 阻擋把多個各自被授權的部分答案,拼湊成一個未被授權的完整全貌 |
| 預設值 | 任何沒有被明確 allowlist 的角色、欄位或組合,一律拒絕存取 |
- 把幾個分別授權的局部答案,在對話中逐步拼成一個完整、未授權的全貌。
- 用「只是為了 debug」或「只是想確認格式」當理由,要求看到完整原始列而不是彙總結果。
- 透過不斷改變篩選條件,一欄一欄地把原本被遮蔽的欄位內容試出來。
- 要求把結果輸出成可下載檔案,藉此跳過畫面上本來會做的欄位遮蔽。
使用者先問 A 部門的平均值,再問 B 部門的平均值,再問「這兩個部門加起來的總筆數」;如果把三個分別授權的答案接在一起,就能反推出一個使用者原本不該直接看到的欄位值。Row/column security 必須考慮這種跨多次請求拼湊出來的外洩路徑,而不是只檢查單次查詢。
注意
一位學員試圖把請求包裝得非常禮貌、非常具體,讓它「看起來理所當然該被回答」,藉此想從 agent 取得該角色平常看不到的欄位。Agent 應該因為請求措辭得體、範圍明確就照做嗎?