ai-security-hardening

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

AI Security Hardening

AI安全强化

Secure LLM and AI systems against prompt injection, jailbreaks, data leakage, and supply chain threats in production environments.
在生产环境中保护LLM和AI系统免受提示注入、越狱攻击、数据泄露及供应链威胁。

When to Use This Skill

何时使用此技能

Use this skill when:
  • Deploying an LLM-powered application handling sensitive user data
  • Protecting against prompt injection attacks in AI agents
  • Implementing output filtering and content moderation
  • Securing model weights and API endpoints from theft
  • Achieving SOC2 or ISO 27001 compliance for AI systems
在以下场景使用此技能:
  • 部署处理敏感用户数据的LLM驱动应用
  • 防范AI Agent中的提示注入攻击
  • 实现输出过滤与内容审核
  • 保护模型权重和API端点免遭窃取
  • 使AI系统达到SOC2或ISO 27001合规标准

AI-Specific Threat Model

AI专属威胁模型

Threat                    Risk                          Control
─────────────────────────────────────────────────────────────────────
Prompt injection          System prompt override         Input sanitization, separate context
Data exfiltration         PII in model outputs           Output filtering, DLP scanning
Jailbreaking             Policy bypass                  Content moderation, guardrails
Model theft               Weight extraction via API      Rate limiting, access controls
Training data poisoning   Backdoored fine-tuned model    Dataset validation, provenance
Supply chain attack       Malicious model weights        Signature verification, scanning
Insecure output           XSS/SQLi from LLM response     Output encoding, parameterized queries
Threat                    Risk                          Control
─────────────────────────────────────────────────────────────────────
Prompt injection          System prompt override         Input sanitization, separate context
Data exfiltration         PII in model outputs           Output filtering, DLP scanning
Jailbreaking             Policy bypass                  Content moderation, guardrails
Model theft               Weight extraction via API      Rate limiting, access controls
Training data poisoning   Backdoored fine-tuned model    Dataset validation, provenance
Supply chain attack       Malicious model weights        Signature verification, scanning
Insecure output           XSS/SQLi from LLM response     Output encoding, parameterized queries

Prompt Injection Defense

提示注入防御

python
import re
from typing import Optional

INJECTION_PATTERNS = [
    r"ignore\s+(all\s+)?(previous|prior|above)\s+instructions",
    r"you\s+are\s+now\s+",
    r"new\s+instructions?:",
    r"system\s+prompt",
    r"forget\s+everything",
    r"act\s+as\s+",
    r"jailbreak",
    r"dan\s+mode",
    r"<\s*system\s*>",
    r"\[INST\]",
]

def detect_prompt_injection(user_input: str) -> tuple[bool, Optional[str]]:
    """Return (is_suspicious, matched_pattern)."""
    normalized = user_input.lower().strip()
    for pattern in INJECTION_PATTERNS:
        if re.search(pattern, normalized, re.IGNORECASE):
            return True, pattern
    return False, None

def sanitize_user_input(user_input: str, max_length: int = 4000) -> str:
    """Sanitize input before passing to LLM."""
    # Truncate
    user_input = user_input[:max_length]

    # Remove null bytes and control characters
    user_input = re.sub(r'[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]', '', user_input)

    # Check for injection
    suspicious, pattern = detect_prompt_injection(user_input)
    if suspicious:
        raise ValueError(f"Potential prompt injection detected: {pattern}")

    return user_input
python
import re
from typing import Optional

INJECTION_PATTERNS = [
    r"ignore\s+(all\s+)?(previous|prior|above)\s+instructions",
    r"you\s+are\s+now\s+",
    r"new\s+instructions?:",
    r"system\s+prompt",
    r"forget\s+everything",
    r"act\s+as\s+",
    r"jailbreak",
    r"dan\s+mode",
    r"<\s*system\s*>",
    r"\[INST\]",
]

def detect_prompt_injection(user_input: str) -> tuple[bool, Optional[str]]:
    """Return (is_suspicious, matched_pattern)."""
    normalized = user_input.lower().strip()
    for pattern in INJECTION_PATTERNS:
        if re.search(pattern, normalized, re.IGNORECASE):
            return True, pattern
    return False, None

def sanitize_user_input(user_input: str, max_length: int = 4000) -> str:
    """Sanitize input before passing to LLM."""
    # Truncate
    user_input = user_input[:max_length]

    # Remove null bytes and control characters
    user_input = re.sub(r'[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]', '', user_input)

    # Check for injection
    suspicious, pattern = detect_prompt_injection(user_input)
    if suspicious:
        raise ValueError(f"Potential prompt injection detected: {pattern}")

    return user_input

Guardrails with NeMo Guardrails

使用NeMo Guardrails构建安全护栏

python
undefined
python
undefined

guardrails.yaml

guardrails.yaml

from nemoguardrails import RailsConfig, LLMRails
config = RailsConfig.from_path("./guardrails-config") rails = LLMRails(config)
async def safe_llm_call(user_message: str) -> str: response = await rails.generate_async( messages=[{"role": "user", "content": user_message}] ) return response["content"]

```yaml
from nemoguardrails import RailsConfig, LLMRails
config = RailsConfig.from_path("./guardrails-config") rails = LLMRails(config)
async def safe_llm_call(user_message: str) -> str: response = await rails.generate_async( messages=[{"role": "user", "content": user_message}] ) return response["content"]

```yaml

guardrails-config/config.yml

guardrails-config/config.yml

models:
  • type: main engine: openai model: gpt-4o-mini
rails: input: flows: - check jailbreak - check sensitive data output: flows: - check output for PII - check output for harmful content
undefined
models:
  • type: main engine: openai model: gpt-4o-mini
rails: input: flows: - check jailbreak - check sensitive data output: flows: - check output for PII - check output for harmful content
undefined

Output Filtering & PII Scrubbing

输出过滤与PII清理

python
import re
from presidio_analyzer import AnalyzerEngine
from presidio_anonymizer import AnonymizerEngine

analyzer = AnalyzerEngine()
anonymizer = AnonymizerEngine()

PII_ENTITIES = ["PERSON", "EMAIL_ADDRESS", "PHONE_NUMBER", "CREDIT_CARD",
                "US_SSN", "IBAN_CODE", "IP_ADDRESS", "LOCATION"]

def scrub_pii_from_output(text: str) -> str:
    """Remove PII from LLM output before returning to user."""
    results = analyzer.analyze(text=text, entities=PII_ENTITIES, language="en")
    if not results:
        return text
    anonymized = anonymizer.anonymize(text=text, analyzer_results=results)
    return anonymized.text

def validate_output_safety(output: str) -> bool:
    """Check output doesn't contain prompt injection artifacts."""
    dangerous_patterns = [
        r"<\s*script\s*>",         # XSS
        r"javascript:",             # XSS
        r";\s*(DROP|DELETE|INSERT)",# SQLi
        r"\$\{.*\}",               # template injection
        r"`.*`",                   # command injection in some contexts
    ]
    for pattern in dangerous_patterns:
        if re.search(pattern, output, re.IGNORECASE):
            return False
    return True
python
import re
from presidio_analyzer import AnalyzerEngine
from presidio_anonymizer import AnonymizerEngine

analyzer = AnalyzerEngine()
anonymizer = AnonymizerEngine()

PII_ENTITIES = ["PERSON", "EMAIL_ADDRESS", "PHONE_NUMBER", "CREDIT_CARD",
                "US_SSN", "IBAN_CODE", "IP_ADDRESS", "LOCATION"]

def scrub_pii_from_output(text: str) -> str:
    """Remove PII from LLM output before returning to user."""
    results = analyzer.analyze(text=text, entities=PII_ENTITIES, language="en")
    if not results:
        return text
    anonymized = anonymizer.anonymize(text=text, analyzer_results=results)
    return anonymized.text

def validate_output_safety(output: str) -> bool:
    """Check output doesn't contain prompt injection artifacts."""
    dangerous_patterns = [
        r"<\s*script\s*>",         # XSS
        r"javascript:",             # XSS
        r";\s*(DROP|DELETE|INSERT)",# SQLi
        r"\$\{.*\}",               # template injection
        r"`.*`",                   # command injection in some contexts
    ]
    for pattern in dangerous_patterns:
        if re.search(pattern, output, re.IGNORECASE):
            return False
    return True

API Security for LLM Endpoints

LLM端点的API安全

python
from fastapi import FastAPI, HTTPException, Depends, Request
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
import jwt
import time
from collections import defaultdict

app = FastAPI()
security = HTTPBearer()
python
from fastapi import FastAPI, HTTPException, Depends, Request
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
import jwt
import time
from collections import defaultdict

app = FastAPI()
security = HTTPBearer()

Rate limiting (per API key)

Rate limiting (per API key)

request_counts = defaultdict(list)
def rate_limit(api_key: str, max_requests: int = 100, window_seconds: int = 60): now = time.time() requests = request_counts[api_key] # Remove old requests outside window request_counts[api_key] = [t for t in requests if now - t < window_seconds] if len(request_counts[api_key]) >= max_requests: raise HTTPException(status_code=429, detail="Rate limit exceeded") request_counts[api_key].append(now)
async def verify_token( credentials: HTTPAuthorizationCredentials = Depends(security) ) -> dict: try: payload = jwt.decode(credentials.credentials, SECRET_KEY, algorithms=["HS256"]) rate_limit(payload["sub"]) return payload except jwt.ExpiredSignatureError: raise HTTPException(status_code=401, detail="Token expired") except jwt.InvalidTokenError: raise HTTPException(status_code=401, detail="Invalid token")
@app.post("/v1/chat/completions") async def chat(request: Request, token: dict = Depends(verify_token)): body = await request.json()
# Input validation
user_msg = body.get("messages", [{}])[-1].get("content", "")
try:
    safe_input = sanitize_user_input(user_msg)
except ValueError as e:
    raise HTTPException(status_code=400, detail=str(e))

# Call LLM and scrub output
response = await call_llm(safe_input, token["scope"])
response["choices"][0]["message"]["content"] = scrub_pii_from_output(
    response["choices"][0]["message"]["content"]
)
return response
undefined
request_counts = defaultdict(list)
def rate_limit(api_key: str, max_requests: int = 100, window_seconds: int = 60): now = time.time() requests = request_counts[api_key] # Remove old requests outside window request_counts[api_key] = [t for t in requests if now - t < window_seconds] if len(request_counts[api_key]) >= max_requests: raise HTTPException(status_code=429, detail="Rate limit exceeded") request_counts[api_key].append(now)
async def verify_token( credentials: HTTPAuthorizationCredentials = Depends(security) ) -> dict: try: payload = jwt.decode(credentials.credentials, SECRET_KEY, algorithms=["HS256"]) rate_limit(payload["sub"]) return payload except jwt.ExpiredSignatureError: raise HTTPException(status_code=401, detail="Token expired") except jwt.InvalidTokenError: raise HTTPException(status_code=401, detail="Invalid token")
@app.post("/v1/chat/completions") async def chat(request: Request, token: dict = Depends(verify_token)): body = await request.json()
# Input validation
user_msg = body.get("messages", [{}])[-1].get("content", "")
try:
    safe_input = sanitize_user_input(user_msg)
except ValueError as e:
    raise HTTPException(status_code=400, detail=str(e))

# Call LLM and scrub output
response = await call_llm(safe_input, token["scope"])
response["choices"][0]["message"]["content"] = scrub_pii_from_output(
    response["choices"][0]["message"]["content"]
)
return response
undefined

Model Weight Security

模型权重安全

bash
undefined
bash
undefined

Verify model weights with SHA-256 hash before loading

Verify model weights with SHA-256 hash before loading

MODEL_DIR="./models/llama-3.1-8b" EXPECTED_HASH="sha256:abc123..."
MODEL_DIR="./models/llama-3.1-8b" EXPECTED_HASH="sha256:abc123..."

Generate hash of downloaded model

Generate hash of downloaded model

actual_hash=$(find "$MODEL_DIR" -name "*.safetensors" | sort | xargs sha256sum | sha256sum) echo "Model hash: $actual_hash"
actual_hash=$(find "$MODEL_DIR" -name "*.safetensors" | sort | xargs sha256sum | sha256sum) echo "Model hash: $actual_hash"

Compare (automate in CI/CD)

Compare (automate in CI/CD)

if [ "$actual_hash" != "$EXPECTED_HASH" ]; then echo "ERROR: Model hash mismatch — possible tampering!" exit 1 fi
if [ "$actual_hash" != "$EXPECTED_HASH" ]; then echo "ERROR: Model hash mismatch — possible tampering!" exit 1 fi

Scan model files for embedded malware (ModelScan)

Scan model files for embedded malware (ModelScan)

pip install modelscan modelscan scan -p "$MODEL_DIR"
undefined
pip install modelscan modelscan scan -p "$MODEL_DIR"
undefined

Network Isolation for AI Services

AI服务的网络隔离

yaml
undefined
yaml
undefined

Kubernetes NetworkPolicy — isolate LLM API

Kubernetes NetworkPolicy — isolate LLM API

apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: llm-api-isolation namespace: ai-services spec: podSelector: matchLabels: app: vllm policyTypes:
  • Ingress
  • Egress ingress:
  • from:
    • namespaceSelector: matchLabels: name: backend # only backend can call LLM ports:
    • protocol: TCP port: 8000 egress:
  • to:
    • namespaceSelector: matchLabels: name: monitoring # metrics only ports:
    • protocol: TCP port: 9090

Block egress to internet — prevent data exfiltration

(allow only internal cluster traffic)

undefined
apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: llm-api-isolation namespace: ai-services spec: podSelector: matchLabels: app: vllm policyTypes:
  • Ingress
  • Egress ingress:
  • from:
    • namespaceSelector: matchLabels: name: backend # only backend can call LLM ports:
    • protocol: TCP port: 8000 egress:
  • to:
    • namespaceSelector: matchLabels: name: monitoring # metrics only ports:
    • protocol: TCP port: 9090

Block egress to internet — prevent data exfiltration

(allow only internal cluster traffic)

undefined

Audit Logging

审计日志

python
import structlog
from datetime import datetime, timezone

audit_log = structlog.get_logger("ai.audit")

def log_llm_interaction(
    user_id: str,
    session_id: str,
    model: str,
    prompt_tokens: int,
    completion_tokens: int,
    was_filtered: bool,
    injection_detected: bool,
):
    audit_log.info(
        "llm_interaction",
        timestamp=datetime.now(timezone.utc).isoformat(),
        user_id=user_id,
        session_id=session_id,
        model=model,
        prompt_tokens=prompt_tokens,
        completion_tokens=completion_tokens,
        was_filtered=was_filtered,
        injection_detected=injection_detected,
        # DO NOT log prompt/completion content — PII risk
    )
python
import structlog
from datetime import datetime, timezone

audit_log = structlog.get_logger("ai.audit")

def log_llm_interaction(
    user_id: str,
    session_id: str,
    model: str,
    prompt_tokens: int,
    completion_tokens: int,
    was_filtered: bool,
    injection_detected: bool,
):
    audit_log.info(
        "llm_interaction",
        timestamp=datetime.now(timezone.utc).isoformat(),
        user_id=user_id,
        session_id=session_id,
        model=model,
        prompt_tokens=prompt_tokens,
        completion_tokens=completion_tokens,
        was_filtered=was_filtered,
        injection_detected=injection_detected,
        # DO NOT log prompt/completion content — PII risk
    )

Common Issues

常见问题

IssueCauseFix
False positive injection blocksOverly broad regexTune patterns; use ML-based classifier for high-traffic
PII in model outputsModel trained on PII dataAdd Presidio scrubbing to output layer
API key leakageKeys in logs or responsesMask keys in logging; use vault for key storage
Model weight tamperingUnverified downloadsAlways verify SHA-256; use
modelscan
Rate limit bypassPer-IP not per-userRate limit on authenticated user ID, not IP
问题原因解决方法
提示注入误拦截正则表达式过于宽泛调整正则模式;高流量场景使用基于机器学习的分类器
模型输出包含PII模型训练数据包含PII在输出层添加Presidio清理机制
API密钥泄露密钥出现在日志或响应中日志中屏蔽密钥;使用密钥管理库存储密钥
模型权重被篡改未验证的下载包始终验证SHA-256哈希;使用
modelscan
工具
绕过速率限制基于IP而非用户进行限制基于已认证用户ID而非IP进行速率限制

Best Practices

最佳实践

  • Never log raw prompts or completions — they may contain PII or sensitive data.
  • Treat LLM output as untrusted input — always encode before rendering in HTML.
  • Use network policies to prevent LLM pods from making outbound internet calls.
  • Rotate API keys quarterly; use short-lived JWT tokens for service-to-service auth.
  • Run
    modelscan
    on any model downloaded from the internet before serving.
  • 切勿记录原始提示或补全内容——它们可能包含PII或敏感数据。
  • 将LLM输出视为不可信输入——在HTML中渲染前务必进行编码。
  • 使用网络策略阻止LLM Pod发起外部互联网请求。
  • 每季度轮换API密钥;服务间认证使用短期JWT令牌。
  • 任何从互联网下载的模型在部署前都要运行
    modelscan
    扫描。

Related Skills

相关技能

  • hashicorp-vault - Secrets management for API keys
  • network-security - Network-level controls
  • linux-hardening - Host hardening
  • agent-observability - AI audit logging
  • llm-gateway - Centralized access control
  • hashicorp-vault - API密钥的机密信息管理
  • network-security - 网络级管控
  • linux-hardening - 主机安全强化
  • agent-observability - AI审计日志
  • llm-gateway - 集中式访问控制