AI Agent 安全開發指南
基於 OWASP AI Agent Security Cheat Sheet
前言:為什麼 AI Agent 安全至關重要
AI Agent 是由大型語言模型(LLM)驅動的自主系統,能夠進行推理、規劃、使用工具、維護記憶,並採取行動來達成目標。這種擴展的能力引入了超越傳統 LLM 提示注入的獨特安全風險。
隨著 AI Agent 在企業應用中的普及,開發者必須了解並防範這些新興威脅。本指南將幫助您建立安全的 AI Agent 架構,最小化攻擊面。
主要安全風險
在開發 AI Agent 時,您需要特別關注以下九大風險類別:
1. 提示注入攻擊(Prompt Injection)
惡意指令通過使用者輸入或外部資料來源(網站、文件、電子郵件)注入,劫持 Agent 行為。這包括:
- 直接注入:使用者直接在輸入中嵌入惡意指令
- 間接注入:通過 Agent 讀取的外部資料(如網頁、文件)注入惡意指令
2. 工具濫用與權限提升(Tool Abuse & Privilege Escalation)
Agent 利用權限過大的工具執行非預期的操作或存取未授權的資源。攻擊者可能誘導 Agent 使用工具來:
- 存取敏感檔案系統
- 執行未授權的資料庫操作
- 呼叫受限的 API
3. 資料外洩(Data Exfiltration)
敏感資訊通過以下管道洩露:
- 工具呼叫參數
- API 請求
- Agent 輸出回應
- 日誌記錄
4. 記憶體污染(Memory Poisoning)
惡意資料持久化到 Agent 記憶體中,可能:
- 影響未來的會話
- 影響其他使用者
- 建立持久的後門
5. 目標劫持(Goal Hijacking)
操縱 Agent 目標使其服務於攻擊者目的,同時看起來合法。Agent 可能被誘導:
- 執行與原始任務無關的操作
- 優先處理攻擊者的指令
- 忽略安全限制
6. 過度自主(Excessive Autonomy)
Agent 在沒有適當人工監督的情況下執行高影響操作,可能導致:
- 不可逆的資料變更
- 財務損失
- 聲譽損害
7. 級聯故障(Cascading Failures)
多 Agent 系統中被入侵的 Agent 將攻擊傳播到其他 Agent,造成:
- 系統性安全漏洞
- 大規模資料洩露
- 服務中斷
8. 錢包拒絕服務(Denial of Wallet, DoW)
通過無限制的 Agent 迴圈造成過高的 API/運算成本的攻擊:
- 無限迴圈消耗 Token
- 大量不必要的工具呼叫
- 惡意觸發昂貴的運算操作
9. 敏感資料暴露(Sensitive Data Exposure)
個人識別資訊(PII)、憑證或機密資料意外包含在:
- Agent 上下文
- 日誌記錄
- 錯誤訊息
- 除錯輸出
最佳安全實踐
1. 工具安全與最小權限原則
這是 AI Agent 安全的第一道防線。確保每個 Agent 只能存取完成其特定任務所需的最少工具和權限。
核心原則:
- 只授予 Agent 特定任務所需的最少工具
- 實施每個工具的權限範圍(唯讀 vs. 寫入、特定資源)
- 對不同信任等級使用不同的工具集(例如:內部 vs. 面向使用者的 Agent)
- 敏感操作需要明確的工具授權
❌ 錯誤示範:權限過大的工具配置
# 危險:Agent 具有無限制的 shell 存取權限
tools = [
{
"name": "execute_command",
"description": "Execute any shell command",
"allowed_commands": "*" # 無限制!
}
]
✅ 正確示範:使用允許清單限制範圍
# 安全:限制為特定、安全的操作
tools = [
{
"name": "file_reader",
"description": "Read files from the reports directory",
"allowed_paths": ["/app/reports/*"],
"allowed_operations": ["read"],
"blocked_patterns": ["*.env", "*.key", "*.pem", "*secret*"]
}
]
工具授權中介軟體範例
from functools import wraps
SENSITIVE_TOOLS = ["send_email", "execute_code", "database_write", "file_delete"]
def require_confirmation(func):
@wraps(func)
async def wrapper(tool_name, params, context):
if tool_name in SENSITIVE_TOOLS:
if not context.get("user_confirmed"):
return {
"status": "pending_confirmation",
"message": f"Action '{tool_name}' requires user approval",
"params": sanitize_for_display(params)
}
return await func(tool_name, params, context)
return wrapper
2. 輸入驗證與提示注入防禦
所有外部資料都應被視為不可信任,必須在納入 Agent 上下文之前進行驗證和清理。
核心原則:
- 將所有外部資料視為不可信任(使用者訊息、檢索的文件、API 回應、電子郵件)
- 在將外部內容納入 Agent 上下文之前實施輸入清理
- 使用分隔符號和清晰的邊界區分指令和資料
- 對已知的注入模式套用內容過濾
- 考慮使用獨立的 LLM 呼叫來驗證/摘要不可信任的內容
建議的防禦層級:
┌─────────────────────────────────────────┐
│ 使用者輸入 / 外部資料 │
└─────────────────┬───────────────────────┘
▼
┌─────────────────────────────────────────┐
│ 第一層:格式驗證與清理 │
│ - 長度限制 │
│ - 特殊字元過濾 │
│ - 編碼驗證 │
└─────────────────┬───────────────────────┘
▼
┌─────────────────────────────────────────┐
│ 第二層:注入模式偵測 │
│ - 已知攻擊模式比對 │
│ - 指令分隔符號偵測 │
│ - 異常內容標記 │
└─────────────────┬───────────────────────┘
▼
┌─────────────────────────────────────────┐
│ 第三層:上下文隔離 │
│ - 明確的資料/指令邊界 │
│ - 信任等級標記 │
│ - 獨立的驗證 LLM 呼叫 │
└─────────────────┬───────────────────────┘
▼
┌─────────────────────────────────────────┐
│ Agent 上下文 │
└─────────────────────────────────────────┘
3. 記憶體與上下文安全
Agent 的記憶體系統可能成為攻擊者的目標,用於持久化惡意資料或竊取敏感資訊。
核心原則:
- 在儲存到 Agent 記憶體之前驗證和清理資料
- 在使用者/會話之間實施記憶體隔離
- 設定記憶體過期時間和大小限制
- 在持久化之前審核記憶體內容是否有敏感資料
- 對長期記憶使用加密完整性檢查
❌ 錯誤示範:未驗證的記憶體儲存
# 危險:將任意使用者輸入儲存到持久記憶體
def save_memory(agent, user_message, assistant_response):
agent.memory.add({
"user": user_message, # 可能包含注入 payload
"assistant": assistant_response,
"timestamp": datetime.now()
})
✅ 正確示範:經過驗證和隔離的記憶體
import hashlib
from datetime import datetime, timedelta
class SecureAgentMemory:
MAX_MEMORY_ITEMS = 100
MAX_ITEM_LENGTH = 5000
MEMORY_TTL_HOURS = 24
def __init__(self, user_id: str, encryption_key: bytes):
self.user_id = user_id
self.encryption_key = encryption_key
self.memories = []
def add(self, content: str, memory_type: str = "conversation"):
# 驗證內容
if len(content) > self.MAX_ITEM_LENGTH:
content = content[:self.MAX_ITEM_LENGTH]
# 掃描敏感資料模式
if self._contains_sensitive_data(content):
content = self._redact_sensitive_data(content)
# 掃描注入模式
content = self._sanitize_injection_attempts(content)
# 建立具有完整性檢查的記憶體項目
entry = {
"content": content,
"type": memory_type,
"timestamp": datetime.utcnow().isoformat(),
"user_id": self.user_id,
"checksum": self._compute_checksum(content)
}
self.memories.append(entry)
self._enforce_limits()
def _contains_sensitive_data(self, content: str) -> bool:
sensitive_patterns = [
r'\b\d{3}-\d{2}-\d{4}\b', # SSN
r'\b\d{16}\b', # 信用卡
r'password\s*[:=]\s*\S+', # 密碼
r'api[_-]?key\s*[:=]\s*\S+' # API 金鑰
]
return any(re.search(p, content, re.I) for p in sensitive_patterns)
def _compute_checksum(self, content: str) -> str:
return hashlib.sha256(
(content + self.user_id).encode() + self.encryption_key
).hexdigest()[:16]
4. 人機協作控制(Human-in-the-Loop)
對於高影響或不可逆的操作,必須確保有人工審核和批准的機制。
核心原則:
- 高影響或不可逆操作需要明確批准
- 在執行前實施操作預覽
- 根據操作風險等級設定自主權邊界
- 提供 Agent 決策和操作的清晰稽核軌跡
- 允許使用者中斷和回滾 Agent 操作
風險等級分類
| 風險等級 | 操作類型 | 處理方式 |
|---|---|---|
| 低(LOW) | 讀取操作、安全查詢 | 自動批准 |
| 中(MEDIUM) | 寫入操作、API 呼叫 | 記錄並監控 |
| 高(HIGH) | 財務、刪除、外部通訊 | 需要人工審核 |
| 嚴重(CRITICAL) | 不可逆、安全敏感操作 | 強制人工批准 |
操作分類與批准流程範例
from enum import Enum
from dataclasses import dataclass
class RiskLevel(Enum):
LOW = "low" # 讀取操作、安全查詢
MEDIUM = "medium" # 寫入操作、API 呼叫
HIGH = "high" # 財務、刪除、外部通訊
CRITICAL = "critical" # 不可逆、安全敏感
ACTION_RISK_MAPPING = {
"search_documents": RiskLevel.LOW,
"read_file": RiskLevel.LOW,
"write_file": RiskLevel.MEDIUM,
"send_email": RiskLevel.HIGH,
"execute_code": RiskLevel.HIGH,
"database_delete": RiskLevel.CRITICAL,
"transfer_funds": RiskLevel.CRITICAL,
}
@dataclass
class PendingAction:
action_id: str
tool_name: str
parameters: dict
risk_level: RiskLevel
explanation: str
class HumanInTheLoopController:
def __init__(self, auto_approve_threshold: RiskLevel = RiskLevel.LOW):
self.auto_approve_threshold = auto_approve_threshold
self.pending_actions = {}
async def request_action(self, tool_name: str, params: dict,
explanation: str) -> dict:
risk_level = ACTION_RISK_MAPPING.get(tool_name, RiskLevel.HIGH)
# 自動批准低風險操作
if risk_level.value <= self.auto_approve_threshold.value:
return {"approved": True, "auto": True}
# 排隊等待人工審核
action = PendingAction(
action_id=generate_uuid(),
tool_name=tool_name,
parameters=self._sanitize_params_for_display(params),
risk_level=risk_level,
explanation=explanation
)
self.pending_actions[action.action_id] = action
return {
"approved": False,
"pending": True,
"action_id": action.action_id,
"requires": "human_approval",
"risk_level": risk_level.value,
"preview": self._generate_action_preview(action)
}
5. 輸出驗證與防護欄
Agent 的輸出必須經過驗證,以防止敏感資料洩露和惡意操作執行。
核心原則:
- 在執行或顯示前驗證 Agent 輸出
- 實施敏感資料洩露的輸出過濾
- 盡可能使用具有 schema 驗證的結構化輸出
- 設定輸出操作的邊界(速率限制、範圍限制)
- 對生成的回應套用內容安全過濾器
輸出驗證管道範例
from pydantic import BaseModel, validator
from typing import Optional, List
class AgentToolCall(BaseModel):
tool_name: str
parameters: dict
reasoning: Optional[str]
@validator('tool_name')
def validate_tool_allowed(cls, v):
allowed_tools = ["search", "read_file", "calculator", "get_weather"]
if v not in allowed_tools:
raise ValueError(f"Tool '{v}' is not in allowed list")
return v
@validator('parameters')
def validate_no_sensitive_data(cls, v):
sensitive_patterns = [
r'api[_-]?key', r'password', r'secret', r'token',
r'credential', r'private[_-]?key'
]
params_str = json.dumps(v).lower()
for pattern in sensitive_patterns:
if re.search(pattern, params_str):
raise ValueError("Parameters contain potentially sensitive data")
return v
class OutputGuardrails:
def __init__(self):
self.pii_patterns = self._load_pii_patterns()
self.rate_limiter = RateLimiter(max_calls=100, window_seconds=60)
async def validate_output(self, agent_output: dict) -> dict:
# 檢查速率限制
if not self.rate_limiter.allow():
raise RateLimitExceeded("Agent action rate limit exceeded")
# 驗證結構
if "tool_calls" in agent_output:
for call in agent_output["tool_calls"]:
validated = AgentToolCall(**call)
# 從回應中過濾 PII
if "response" in agent_output:
agent_output["response"] = self._filter_pii(agent_output["response"])
# 檢查資料外洩模式
if self._detect_exfiltration_attempt(agent_output):
raise SecurityViolation("Potential data exfiltration detected")
return agent_output
def _detect_exfiltration_attempt(self, output: dict) -> bool:
"""偵測通過工具呼叫外洩資料的嘗試"""
suspicious_patterns = [
# 在 URL 中編碼敏感資料
lambda o: "http" in str(o) and any(
p in str(o).lower() for p in ["base64", "encode", "password"]
),
# webhook/API 呼叫中的大量資料
lambda o: o.get("tool_name") in ["http_request", "webhook"] and
len(str(o.get("parameters", ""))) > 10000,
]
return any(pattern(output) for pattern in suspicious_patterns)
6. 監控與可觀測性
全面的監控系統是偵測和回應安全事件的關鍵。
核心原則:
- 記錄所有 Agent 決策、工具呼叫和結果
- 實施異常行為的異常偵測
- 追蹤每個會話/使用者的 Token 使用量和成本
- 設定安全相關事件的警報
- 維護合規和鑑識的稽核軌跡
建議的異常偵測閾值
| 指標 | 建議閾值 |
|---|---|
| 每分鐘工具呼叫次數 | 30 |
| 失敗的工具呼叫次數 | 5 |
| 注入嘗試次數 | 1(立即警報) |
| 敏感資料存取次數 | 3 |
| 每會話成本(美元) | $10.00 |
Agent 監控範例
import structlog
from dataclasses import dataclass
from typing import Dict, Any, Optional
from datetime import datetime
logger = structlog.get_logger()
@dataclass
class AgentSecurityEvent:
event_type: str
severity: str # INFO, WARNING, CRITICAL
agent_id: str
session_id: str
user_id: str
timestamp: datetime
details: Dict[str, Any]
tool_name: Optional[str] = None
class AgentMonitor:
ANOMALY_THRESHOLDS = {
"tool_calls_per_minute": 30,
"failed_tool_calls": 5,
"injection_attempts": 1,
"sensitive_data_access": 3,
"cost_per_session_usd": 10.0,
}
def __init__(self, agent_id: str):
self.agent_id = agent_id
self.session_metrics = {}
self.alert_handlers = []
async def log_tool_call(self, session_id: str, tool_name: str,
params: dict, result: dict, user_id: str):
# 記錄前遮蔽敏感資料
safe_params = self._redact_sensitive(params)
safe_result = self._redact_sensitive(result)
event = AgentSecurityEvent(
event_type="tool_call",
severity="INFO",
agent_id=self.agent_id,
session_id=session_id,
user_id=user_id,
timestamp=datetime.utcnow(),
tool_name=tool_name,
details={
"parameters": safe_params,
"result_status": result.get("status"),
"execution_time_ms": result.get("execution_time_ms"),
}
)
await self._emit_event(event)
await self._check_anomalies(session_id, event)
def _redact_sensitive(self, data: dict) -> dict:
"""從日誌資料中遮蔽敏感欄位"""
sensitive_keys = {"password", "api_key", "token", "secret", "credential"}
def redact(obj):
if isinstance(obj, dict):
return {
k: "***REDACTED***" if k.lower() in sensitive_keys else redact(v)
for k, v in obj.items()
}
elif isinstance(obj, list):
return [redact(i) for i in obj]
return obj
return redact(data)
7. 多 Agent 系統安全
當多個 Agent 協作時,需要特別注意防止攻擊在 Agent 之間傳播。
核心原則:
- 在 Agent 之間實施信任邊界
- 驗證和清理 Agent 間通訊
- 防止通過 Agent 鏈的權限提升
- 隔離 Agent 執行環境
- 套用熔斷器以防止級聯故障
Agent 信任等級定義
| 信任等級 | 說明 |
|---|---|
| UNTRUSTED (0) | 不受信任的外部 Agent,需要完全驗證 |
| INTERNAL (1) | 內部 Agent,有基本存取權限 |
| PRIVILEGED (2) | 具有提升權限的 Agent |
| SYSTEM (3) | 系統級 Agent,具有完全存取權限 |
安全的多 Agent 通訊範例
from typing import Optional, List
import jwt
from datetime import datetime, timedelta
from enum import Enum
class AgentTrustLevel(Enum):
UNTRUSTED = 0
INTERNAL = 1
PRIVILEGED = 2
SYSTEM = 3
class SecureAgentBus:
"""多 Agent 系統的安全通訊層"""
def __init__(self, signing_key: bytes):
self.signing_key = signing_key
self.agent_registry = {}
self.circuit_breakers = {}
def register_agent(self, agent_id: str, trust_level: AgentTrustLevel,
allowed_recipients: List[str]):
self.agent_registry[agent_id] = {
"trust_level": trust_level,
"allowed_recipients": allowed_recipients,
"allowed_message_types": self._get_allowed_types(trust_level)
}
self.circuit_breakers[agent_id] = CircuitBreaker(
failure_threshold=5,
recovery_timeout=60
)
async def send_message(self, sender_id: str, recipient_id: str,
message_type: str, payload: dict) -> dict:
# 驗證發送者
sender = self.agent_registry.get(sender_id)
if not sender:
raise SecurityViolation(f"Unknown sender agent: {sender_id}")
# 檢查熔斷器
if self.circuit_breakers[sender_id].is_open:
raise CircuitBreakerOpen(f"Agent {sender_id} is temporarily blocked")
# 驗證接收者授權
if recipient_id not in sender["allowed_recipients"]:
raise SecurityViolation("Sender not authorized to message recipient")
# 清理 payload
sanitized_payload = self._sanitize_payload(payload, sender["trust_level"])
# 建立簽署的訊息
signed_message = {
"sender": sender_id,
"recipient": recipient_id,
"type": message_type,
"payload": sanitized_payload,
"timestamp": datetime.utcnow().isoformat(),
"signature": self._sign_message(sender_id, recipient_id,
message_type, sanitized_payload)
}
return signed_message
async def receive_message(self, recipient_id: str, message: dict) -> dict:
# 驗證簽章
if not self._verify_signature(message):
raise SecurityViolation("Invalid message signature")
# 檢查訊息新鮮度(防止重播攻擊)
msg_time = datetime.fromisoformat(message["timestamp"])
if (datetime.utcnow() - msg_time) > timedelta(minutes=5):
raise SecurityViolation("Message expired (possible replay attack)")
return message["payload"]
8. 資料保護與隱私
遵循資料最小化原則,確保敏感資料得到適當保護。
核心原則:
- 最小化 Agent 上下文中的敏感資料
- 實施資料分類和處理規則
- 對靜態和傳輸中的資料套用加密
- 執行資料保留和刪除政策
- 符合隱私法規(GDPR、CCPA 等)
資料分類與處理建議
| 分類 | 範例 | 處理方式 |
|---|---|---|
| RESTRICTED | SSN、信用卡、健康資料 | 完全遮蔽 |
| CONFIDENTIAL | 薪資、API 金鑰、密碼 | 部分遮蔽 |
| INTERNAL | 內部文件、草稿 | 正常處理,記錄時遮蔽 |
| PUBLIC | 公開資訊 | 正常處理 |
資料分類與處理範例
from enum import Enum
import re
class DataClassification(Enum):
PUBLIC = "public"
INTERNAL = "internal"
CONFIDENTIAL = "confidential"
RESTRICTED = "restricted" # PII、財務、健康
class DataProtectionPolicy:
def __init__(self):
self.classification_rules = []
self.handling_rules = {}
def classify_data(self, data: str, context: dict) -> DataClassification:
"""根據內容模式自動分類資料"""
patterns = {
DataClassification.RESTRICTED: [
r'\b\d{3}-\d{2}-\d{4}\b', # SSN
r'\b\d{16}\b', # 信用卡
r'\b[A-Z]{2}\d{6,9}\b', # 護照
r'diagnosis|prescription|patient', # 健康
],
DataClassification.CONFIDENTIAL: [
r'salary|compensation|bonus',
r'api[_-]?key|password|secret',
r'confidential|internal only',
],
DataClassification.INTERNAL: [
r'@company\.com',
r'internal|draft|not for distribution',
]
}
for classification, pattern_list in patterns.items():
if any(re.search(p, data, re.I) for p in pattern_list):
return classification
return DataClassification.PUBLIC
def apply_protection(self, data: str, classification: DataClassification,
operation: str) -> str:
"""根據分類套用適當的保護"""
handlers = {
DataClassification.RESTRICTED: {
"include_in_context": self._redact_fully,
"log": self._redact_fully,
"output": self._redact_fully,
},
DataClassification.CONFIDENTIAL: {
"include_in_context": self._mask_partially,
"log": self._redact_fully,
"output": self._mask_partially,
},
DataClassification.INTERNAL: {
"include_in_context": lambda x: x,
"log": self._mask_partially,
"output": lambda x: x,
},
}
handler = handlers.get(classification, {}).get(operation, lambda x: x)
return handler(data)
def _redact_fully(self, data: str) -> str:
return "[REDACTED]"
def _mask_partially(self, data: str) -> str:
if len(data) <= 4:
return "****"
return data[:2] + "*" * (len(data) - 4) + data[-2:]
應該做與不應該做的事
✅ 應該做
- 對所有 Agent 工具和權限套用最小權限原則
- 驗證和清理所有外部輸入(使用者訊息、文件、API 回應)
- 對高風險操作實施人機協作控制
- 在使用者/會話之間隔離記憶體和上下文
- 監控 Agent 行為並設定異常偵測
- 使用具有 schema 驗證的結構化輸出
- 簽署和驗證 Agent 間通訊
- 分類資料並套用適當的保護
❌ 不應該做
- 給予 Agent 無限制的工具存取或萬用字元權限
- 信任來自外部來源的內容(網站、電子郵件、文件)
- 允許 Agent 在沒有沙箱的情況下執行任意程式碼
- 在 Agent 記憶體中儲存敏感資料而不加密/遮蔽
- 讓 Agent 在沒有人工監督的情況下做出高影響決策
- 忽略成本控制(無限迴圈可能導致錢包拒絕服務)
- 在多 Agent 系統中傳遞未清理的資料
- 以明文記錄敏感資料(PII、憑證)
安全實施檢查清單
在部署 AI Agent 之前,請確保完成以下檢查項目:
工具安全
- [ ] 已實施最小權限原則
- [ ] 所有工具都有允許清單/阻擋清單
輸入驗證
- [ ] 已實施輸入清理
- [ ] 已設定注入模式過濾
記憶體安全
- [ ] 已實施使用者/會話隔離
- [ ] 已設定記憶體過期和大小限制
人機協作
- [ ] 高風險操作需要人工批准
- [ ] 已實施操作預覽功能
輸出驗證
- [ ] 已實施敏感資料過濾
- [ ] 已設定速率限制
監控
- [ ] 已記錄所有 Agent 操作
- [ ] 已設定異常偵測警報
多 Agent 安全
- [ ] 已實施 Agent 間信任邊界
- [ ] 已驗證 Agent 間通訊
資料保護
- [ ] 已實施資料分類
- [ ] 敏感資料已加密
參考資源
以下資源可幫助您深入了解 AI Agent 安全:
- OWASP LLM Top 10
- OWASP AI Agent Security Cheat Sheet
- OWASP LLM Prompt Injection Prevention Cheat Sheet
- NIST AI Risk Management Framework
- OpenAI Safety Best Practices
- Google Secure AI Framework (SAIF)
本文件基於 OWASP AI Agent Security Cheat Sheet 編譯
建議定期檢視並更新安全實踐以應對新興威脅
