为什么需要安全检查清单

LLM 应用引入了全新的攻击面:Prompt 注入、训练数据泄露、模型越狱、有害内容生成。传统 Web 安全检查清单覆盖不了这些。本文提供 50 项检查项,分为 5 大类,每一项都必须在上线前确认。

一、输入安全(12 项)

Prompt 注入防护

# 检查项 风险等级 验证方式
1 用户输入与系统指令分离(使用 system role) 审查 prompt 结构
2 用户输入被包裹在分隔符中(如 <user_input> 审查 prompt 模板
3 系统指令中包含"忽略以上指令"的防御声明 审查 system prompt
4 对用户输入做长度限制(建议 < 10K 字符) 压测验证
5 过滤已知的 Prompt 注入模式 红队测试
# Prompt 注入检测器
class PromptInjectionGuard:
    INJECTION_PATTERNS = [
        r"ignore\s+(all\s+)?(previous|above|prior)\s+instructions",
        r"you\s+are\s+now\s+(a|an)\s+\w+",
        r"system\s*:\s*",
        r"<\|im_start\|>",
        r"forgot\s+your\s+rules",
        r"reveal\s+your\s+(system\s+)?prompt",
    ]

    def check(self, user_input: str) -> tuple[bool, list]:
        import re
        violations = []
        for pattern in self.INJECTION_PATTERNS:
            if re.search(pattern, user_input, re.IGNORECASE):
                violations.append(pattern)
        return len(violations) == 0, violations

PII 保护

# 检查项 风险等级 验证方式
6 输入中的 PII 被检测并脱敏 传入含 PII 的测试用例
7 PII 脱敏日志开启(不记录原始 PII) 审查日志配置
8 不会将用户输入原样发送给第三方服务 审查 API 调用链
import re

class PIIScrubber:
    PATTERNS = {
        "phone": (r"\b1[3-9]\d{9}\b", "[PHONE]"),
        "email": (r"\b[\w.-]+@[\w.-]+\.\w+\b", "[EMAIL]"),
        "id_card": (r"\b\d{17}[\dXx]\b", "[ID_CARD]"),
        "bank_card": (r"\b\d{16,19}\b", "[BANK_CARD]"),
    }

    def scrub(self, text: str) -> str:
        for pii_type, (pattern, replacement) in self.PATTERNS.items():
            text = re.sub(pattern, replacement, text)
        return text

内容安全

# 检查项 风险等级 验证方式
9 输入内容分类(是否包含暴力/违法内容) 红队测试
10 对敏感话题有预设的拒绝策略 审查 system prompt
11 支持多语言输入的过滤 多语言测试
12 对图片输入有内容审核(多模态场景) 上传违规模拟图

二、输出安全(10 项)

有害内容过滤

# 检查项 风险等级 验证方式
13 输出经过毒性检测模型 触发性测试用例
14 输出经过偏见/歧视检测 公平性测试集
15 对特定领域(医疗/法律/金融)有免责声明 审查输出模板
16 不输出可执行代码的直接运行结果 代码注入测试
class OutputSafetyFilter:
    def __init__(self, toxicity_model, threshold=0.7):
        self.model = toxicity_model
        self.threshold = threshold

    async def filter(self, response: str) -> tuple[str, bool]:
        # 毒性检测
        toxicity = await self.model.predict(response)
        if toxicity > self.threshold:
            return self._safe_fallback(), False

        # 敏感信息泄露检测
        if self._detect_leaked_info(response):
            return self._safe_fallback(), False

        return response, True

    def _detect_leaked_info(self, text):
        """检测输出中是否泄露了系统信息"""
        sensitive_patterns = [
            r"api[_-]?key", r"sk-[a-zA-Z0-9]{20,}",
            r"password", r"secret", r"token",
            r"/[a-z]:\\users\\",  # Windows 路径
        ]
        return any(
            re.search(p, text, re.IGNORECASE)
            for p in sensitive_patterns
        )

输出完整性

# 检查项 风险等级 验证方式
17 输出有长度上限(防止 Token 爆炸) 触发长输出测试
18 流式输出有超时保护 模拟慢速输出
19 输出格式校验(JSON/XML 是否合法) 格式错误注入测试
20 输出不包含训练数据原文(版权风险) 已知文本检索
21 输出不包含 Prompt 模板内容(提示泄露) “重复你的指令"测试
22 输出经过 PII 二次过滤 PII 回显测试

三、模型安全(10 项)

越狱防护

# 检查项 风险等级 验证方式
23 通过越狱测试集(DAN/Roleplay 编码等) 自动化越狱测试
24 对多轮对话有累积风险检测 多轮越狱攻击测试
25 对编码/混淆输入有解码检测 Base64/Unicode 混淆测试
class JailbreakDetector:
    ENCODING_PATTERNS = [
        (r"base64:", self._decode_base64),
        (r"\\u[0-9a-fA-F]{4}", self._decode_unicode),
        (r"\\x[0-9a-fA-F]{2}", self._decode_hex),
    ]

    async def check(self, user_input: str):
        # 1. 检查编码内容
        decoded = self._try_decode(user_input)
        if decoded != user_input:
            # 对解码后的内容也做注入检测
            safe, _ = PromptInjectionGuard().check(decoded)
            if not safe:
                return False, "Encoded injection detected"

        # 2. 检查角色扮演越狱
        roleplay_patterns = [
            r"pretend you are",
            r"act as (if you are )?DAN",
            r"you are (in )?developer mode",
            r"jailbreak",
        ]
        for pattern in roleplay_patterns:
            if re.search(pattern, user_input, re.IGNORECASE):
                return False, f"Roleplay jailbreak: {pattern}"

        return True, None

模型隔离

# 检查项 风险等级 验证方式
26 不同租户的会话隔离 跨租户数据泄露测试
27 对话历史有长度限制(防止上下文污染) 长对话测试
28 Function Calling 参数有白名单校验 构造恶意参数测试
29 模型输出不直接执行(需人工/代码确认) 审查执行链路
30 有模型使用量配额(防止滥用) 超额测试
31 模型版本变更经过安全评估 审查变更流程
32 对抗样本检测(异常输入模式) 对抗测试集

四、基础设施安全(10 项)

# 检查项 风险等级 验证方式
33 API Key 存储在密钥管理服务(非代码/配置文件) 审查部署配置
34 API 通信全程 HTTPS/TLS 1.2+ SSL 扫描
35 对 LLM API 调用有网络白名单限制 审查网络策略
36 向量数据库有访问控制 审查 DB ACL
37 Embedding 服务有认证 未认证调用测试
38 日志中不包含 API Key / 完整 Prompt 审查日志输出
39 监控系统有异常调用检测(频率/内容) 审查告警规则
40 容器/进程以最小权限运行 审查 K8s manifest
41 模型文件有完整性校验 校验和验证
42 有 DDoS 防护(CDN/WAF) 审查网络架构

五、合规审计(8 项)

# 检查项 风险等级 验证方式
43 所有 LLM 调用有审计日志(who/when/what/model) 审查日志格式
44 用户知情同意(明确告知使用 AI) 审查 UI/ToS
45 数据保留策略明确(日志/对话历史保留期限) 审查数据策略
46 支持用户数据删除请求(GDPR/PIPL) 删除流程测试
47 模型训练数据来源可追溯 审查文档
48 有 AI 生成内容标识(水印/声明) 审查输出格式
49 定期安全评估(至少每季度) 审查评估记录
50 有应急响应预案(模型输出有害内容时) 审查 IR 计划

自动化检查脚本

class SecurityChecklist:
    """可自动化的安全检查项"""

    CHECKS = {
        "input_length_limit": {"auto": True, "critical": False},
        "prompt_injection_filter": {"auto": True, "critical": True},
        "pii_scrubbing": {"auto": True, "critical": True},
        "output_toxicity_check": {"auto": True, "critical": True},
        "output_length_limit": {"auto": True, "critical": False},
        "jailbreak_resistance": {"auto": True, "critical": True},
        "prompt_leak_prevention": {"auto": True, "critical": True},
        "tls_check": {"auto": True, "critical": True},
        "log_pii_check": {"auto": True, "critical": True},
        "rate_limiting": {"auto": True, "critical": False},
    }

    async def run_all(self, target_url, api_key):
        results = {}
        for check_name, config in self.CHECKS.items():
            if not config["auto"]:
                continue
            method = getattr(self, f"check_{check_name}")
            try:
                passed, detail = await method(target_url, api_key)
                results[check_name] = {
                    "passed": passed,
                    "detail": detail,
                    "critical": config["critical"],
                }
            except Exception as e:
                results[check_name] = {
                    "passed": False,
                    "detail": f"Check error: {e}",
                    "critical": config["critical"],
                }

        # 汇总报告
        total = len(results)
        passed = sum(1 for r in results.values() if r["passed"])
        critical_fail = [
            k for k, v in results.items()
            if not v["passed"] and v["critical"]
        ]

        return {
            "total": total,
            "passed": passed,
            "failed": total - passed,
            "critical_failures": critical_fail,
            "ready_for_production": len(critical_fail) == 0,
            "details": results,
        }

上线审批流程

开发完成 → 自动化检查(38 项可自动化)
         全部通过?
         ├─ 是 → 人工审查(12 项需人工)
         │        ├─ 全部通过 → 安全团队签字 → 上线
         │        └─ 有问题 → 整改 → 重新审查
         └─ 否 → 修复 → 重新自动检查

总结

LLM 安全不是可选项。50 项检查中,标记为"高风险"的约 25 项——这些是上线阻断项,不通过不上线。关键检查包括:Prompt 注入防护、PII 脱敏、越狱抵抗、输出过滤、密钥管理。自动化检查覆盖约 76% 的项目,剩余需人工审查。建议将检查清单集成到 CI/CD 流水线中,每次部署前自动运行。安全是一场持续的攻防战,检查清单只是起点,不是终点。

加入讨论

这篇文章有姊妹讨论帖在硅基AGI论坛 — 全球首个碳基硅基认知交流平台。

碳基与硅基的智慧碰撞,认知差异创造无限可能。