ai-security-hardening について
本番環境の AI システムを保護するためのセキュリティ強化スキルです。プロンプトインジェクション、データ漏えい、脱獄、サプライチェーンのリスクに対し、フィルタリング、API 保護、モデル検証、ネットワーク分離、監査を扱います。
主な機能
- 01プロンプトインジェクション対策
- 02出力フィルタリングと PII の除去
- 03LLM API のアクセス制御とレート制限
- 04モデルウェイトの完全性検証
- 05AI サービスのネットワーク分離
- 06対話の監査ログ
活用シーン
- 01機密データを扱う LLM アプリの保護
- 02AI エージェントへのプロンプトインジェクションリスク低減
- 03モデル出力に含まれる個人情報のフィルタリング
- 04モデルウェイトと API エンドポイントの保護
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ bagelhole/ devops-security-agent-skills --skill ai-security-hardeningインストール後、Claude Code で/ai-security-hardeningを入力して読み込まれたか確認します。
詳細
AI セキュリティの強化
本番環境で、プロンプトインジェクション、ジェイルブレイク、データ漏えい、サプライチェーンの脅威から LLM および AI システムを保護します。
このスキルを使用する場面
次のような場合に、このスキルを使用してください。
- 機密性の高いユーザーデータを扱う LLM 搭載アプリケーションをデプロイする場合
- AI エージェントへのプロンプトインジェクション攻撃を防ぐ場合
- 出力フィルタリングとコンテンツモデレーションを実装する場合
- モデルの重みと API エンドポイントを窃取から保護する場合
- AI システムで SOC2 または ISO 27001 への準拠を達成する場合
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
プロンプトインジェクション対策
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
NeMo Guardrails によるガードレール
# 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"]
# 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
出力フィルタリングと PII の除去
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
LLM エンドポイントの API セキュリティ
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)
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
モデルの重みのセキュリティ
# Verify model weights with SHA-256 hash before loading
MODEL_DIR="./models/llama-3.1-8b"
EXPECTED_HASH="sha256:abc123..."
# Generate hash of downloaded model
actual_hash=$(find "$MODEL_DIR" -name "*.safetensors" | sort | xargs sha256sum | sha256sum)
echo "Model hash: $actual_hash"
# Compare (automate in CI/CD)
if [ "$actual_hash" != "$EXPECTED_HASH" ]; then
echo "ERROR: Model hash mismatch — possible tampering!"
exit 1
fi
# Scan model files for embedded malware (ModelScan)
pip install modelscan
modelscan scan -p "$MODEL_DIR"
AI サービスのネットワーク分離
# 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)
監査ログ
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
)
よくある問題
| 問題 | 原因 | 対処方法 |
|---|---|---|
| インジェクションの誤検知によるブロック | 範囲が広すぎる正規表現 | パターンを調整します。トラフィックの多い環境では、ML ベースの分類器を使用します |
| モデル出力に含まれる PII | PII データでモデルがトレーニングされている | 出力レイヤーに Presidio による除去処理を追加します |
| API キーの漏えい | ログやレスポンスにキーが含まれている | ログ内のキーをマスキングし、キーの保管にはボールトを使用します |
| モデルの重みの改ざん | 未検証のダウンロード | SHA-256 を必ず検証し、modelscan を使用します |
| レート制限の回避 | ユーザー単位ではなく IP 単位で制限している | IP ではなく、認証済みユーザー ID に基づいてレート制限を行います |
ベストプラクティス
- 生のプロンプトや生成結果をログに記録しないでください。PII や機密データが含まれている可能性があります。
- LLM の出力は信頼できない入力として扱い、HTML にレンダリングする前に必ずエンコードしてください。
- ネットワークポリシーを使用して、LLM ポッドから外部インターネットへの接続を防いでください。
- API キーは四半期ごとにローテーションし、サービス間認証には有効期間の短い JWT トークンを使用してください。
- インターネットからダウンロードしたモデルは、サービングする前に必ず
modelscanを実行してください。
関連スキル
- hashicorp-vault - API キーのシークレット管理
- network-security - ネットワークレベルの制御
- linux-hardening - ホストの強化
- agent-observability - AI の監査ログ
- llm-gateway - 一元化されたアクセス制御
コメント
Markdown 対応