security について
ユーザーデータ、認証、外部入力を扱うエージェントや開発者向けのセキュリティ標準スキルです。安全なコーディングの指針、実装・レビュー用チェックリスト、検査ルール、シークレットと依存関係の確認スクリプトを備えています。
主な機能
- 01入力検証とインジェクション対策
- 02認証と認可の指針
- 03データ保護とシークレット管理
- 04API、アップロード、ログの保護
- 05実装・コードレビュー用チェックリスト
- 06ルールとセキュリティスキャンスクリプト
活用シーン
- 01実装前のセキュリティ計画
- 02コードのセキュリティ違反のレビュー
- 03リポジトリ内のシークレットと脆弱な依存関係の確認
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ alexanderstephenthompson/ claude-hub --skill securityインストール後、Claude Code で/securityを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
assets/threat-model-template.mdsecurity-checklist.mdreferences/owasp-top-10.mdinput-validation.mdauth-patterns.mdscripts/scan_secrets.pycheck_dependencies.py詳細
セキュリティスキル
バージョン: 1.0 ソース: セキュリティ標準
セキュリティは後から付け足すのではなく、最初から組み込む必要があります。これらの標準は、ユーザーデータ、認証、または外部入力を扱うすべてのコードに適用されます。
問題点
AI エージェントは、安全にコードを作ることよりも、コードを動作させることを優先しがちです。明確なセキュリティ標準がなければ、各セッションでは最短の道が選ばれます。たとえば、クエリの文字列連結、手軽さを優先したシークレットのハードコード、速度を重視した広範な権限付与です。これらは悪意のある選択ではありません。セキュリティがプロンプトに含まれていないとき、最も抵抗の少ない道だからです。これらの標準により、安全なパターンがデフォルトになります。
活用方法
- ビルダー: ユーザー入力、認証、または外部サービスに関わるコードを書く前に、
## ビルダー向けチェックリストを読んでください。セキュリティは設計段階で組み込む必要があり、後から修正するものではありません。 - リファクタリング担当者:
## 適用ルールを使って、セキュリティ違反を見つけてください。修正方法については、説明セクションを読んでください。 - 両者: 説明セクションが正式な標準です。チェックリストとルール表は、同じ内容を簡潔にまとめたものです。
基本原則
- セキュリティ・バイ・デザイン — 後付けではなく、最初からセキュリティを組み込む
- 多層防御 — セキュリティ対策を多層化し、単一障害点をなくす
- 最小権限 — タスクに必要な最小限のアクセス権を付与する
- 安全なフェイル — エラーによって脆弱性や機密データを露呈させない
- ゼロトラスト — 入力を決して信頼せず、常に検証する
- 侵害を前提とする — 攻撃者が侵入することを想定して設計し、被害範囲を最小限に抑える
OWASP Top 10(2021)
防止すべき重大な脆弱性:
| # | 脆弱性 | 防止策 |
|---|---|---|
| 1 | アクセス制御の不備 | すべてのリクエストで認可を確認する |
| 2 | 暗号化の失敗 | 強力な暗号化を使用し、独自の方式を作らない |
| 3 | インジェクション | パラメーター化クエリ、入力検証 |
| 4 | 安全でない設計 | 脅威モデリング、安全なアーキテクチャ |
| 5 | セキュリティ設定の不備 | 安全なデフォルト設定、適切なエラー処理 |
| 6 | 脆弱なコンポーネント | 依存関係を最新に保ち、定期的に監査する |
| 7 | 認証の失敗 | 強力な認証、多要素認証(MFA)、安全なセッション管理 |
| 8 | 完全性の失敗 | コード/データの完全性を検証し、リリースに署名する |
| 9 | ログ記録の失敗 | セキュリティイベントを記録し、ログデータを保護する |
| 10 | SSRF | サーバー側の URL を検証し、許可リストを使用する |
入力検証
基本原則
ユーザー入力を決して信頼しないでください。 すべての入力は悪意のあるものである可能性があります。
検証戦略
1. Validate → 2. Sanitize → 3. Encode (for output context)
SQL インジェクションの防止
# ❌ NEVER - String concatenation
query = f"SELECT * FROM users WHERE id = {user_id}"
# ✅ ALWAYS - Parameterized queries
cursor.execute("SELECT * FROM users WHERE id = ?", (user_id,))
# ✅ ALWAYS - ORM with parameters
User.objects.filter(id=user_id)
XSS の防止
// ❌ NEVER - Direct HTML insertion
element.innerHTML = userInput;
document.write(userInput);
// ✅ ALWAYS - Text content (auto-escapes)
element.textContent = userInput;
// ✅ ALWAYS - Template literals with escaping
const escaped = escapeHtml(userInput);
コマンドインジェクションの防止
# ❌ NEVER - Shell execution with user input
os.system(f"ls {user_path}")
subprocess.call(f"convert {filename}", shell=True)
# ✅ ALWAYS - Argument arrays (no shell)
subprocess.run(["ls", user_path], shell=False)
subprocess.run(["convert", filename], shell=False)
入力検証のパターン
import re
def validate_email(email: str) -> bool:
"""Validate email format."""
pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
return bool(re.match(pattern, email)) and len(email) <= 254
def validate_username(username: str) -> bool:
"""Validate username: alphanumeric, 3-30 chars."""
pattern = r'^[a-zA-Z0-9_]{3,30}$'
return bool(re.match(pattern, username))
def validate_url(url: str, allowed_domains: list[str]) -> bool:
"""Validate URL against allowlist."""
from urllib.parse import urlparse
parsed = urlparse(url)
return (
parsed.scheme in ('http', 'https') and
parsed.netloc in allowed_domains
)
認証と認可
パスワード要件
| 要件 | 最低基準 |
|---|---|
| 長さ | 12 文字 |
| 複雑さ | 長さの要件を満たす場合は不要 |
| よく使われるパスワード | 上位 10,000 件をブロックする |
| 漏えいしたパスワード | HaveIBeenPwned と照合する |
パスワードの保存
# ✅ Use bcrypt, Argon2, or scrypt
import bcrypt
def hash_password(password: str) -> bytes:
"""Hash password with bcrypt."""
salt = bcrypt.gensalt(rounds=12)
return bcrypt.hashpw(password.encode(), salt)
def verify_password(password: str, hashed: bytes) -> bool:
"""Verify password against hash."""
return bcrypt.checkpw(password.encode(), hashed)
絶対にしないでください:
- パスワードを平文で保存する
- パスワードに MD5 または SHA1 を使用する
- 独自のハッシュ方式を作る
セッション管理
# Session security settings
SESSION_CONFIG = {
"cookie_secure": True, # HTTPS only
"cookie_httponly": True, # No JavaScript access
"cookie_samesite": "Lax", # CSRF protection
"session_lifetime": 3600, # 1 hour max
"regenerate_on_login": True # Prevent session fixation
}
認可チェック
# ✅ Check authorization on EVERY request
def get_document(user, document_id):
document = Document.get(document_id)
# Always verify ownership/access
if document.owner_id != user.id and not user.is_admin:
raise PermissionError("Access denied")
return document
データ保護
暗号化要件
| データの種類 | 保存時 | 転送中 |
|---|---|---|
| パスワード | ハッシュ化(bcrypt/Argon2) | TLS 1.2+ |
| 個人を特定できる情報(名前、メールアドレス、住所) | AES-256 | TLS 1.2+ |
| 支払いデータ | AES-256 + PCI DSS | TLS 1.3 |
| 健康データ | AES-256 + HIPAA | TLS 1.3 |
| セッショントークン | 該当なし | TLS 1.2+ |
| API キー | AES-256 | TLS 1.2+ |
機密データの取り扱い
# ✅ Mask sensitive data in logs
def log_user_action(user, action, data):
safe_data = {
"user_id": user.id,
"email": mask_email(user.email), # j***@example.com
"action": action,
# Never log: passwords, tokens, full credit cards
}
logger.info("User action", extra=safe_data)
def mask_email(email: str) -> str:
"""Mask email for logging."""
local, domain = email.split("@")
return f"{local[0]}***@{domain}"
def mask_card(card: str) -> str:
"""Show only last 4 digits."""
return f"****-****-****-{card[-4:]}"
データ保持
- 不要になったデータを削除する
- データの有効期限ポリシーを実装する
- ユーザーデータのエクスポート/削除を可能にする(GDPR)
- 削除済みデータを安全に消去する(削除済みとしてマークするだけでは不十分)
API セキュリティ
レート制限
# Implement rate limiting per endpoint
RATE_LIMITS = {
"/api/login": "5/minute", # Prevent brute force
"/api/register": "3/minute", # Prevent spam
"/api/password-reset": "3/hour",
"/api/*": "100/minute", # General limit
}
CORS 設定
# ✅ Specific origins only
CORS_CONFIG = {
"origins": [
"https://app.example.com",
"https://admin.example.com",
],
"methods": ["GET", "POST", "PUT", "DELETE"],
"allow_credentials": True,
"max_age": 3600,
}
# ❌ NEVER in production
CORS_CONFIG = {
"origins": "*", # Allows any origin!
}
API レスポンスのセキュリティ
# ❌ Never expose internal errors
def handle_error(error):
# Don't send: stack traces, SQL errors, file paths
return {"error": "An error occurred"}, 500
# ✅ Log details, return generic message
def handle_error(error):
logger.error(f"Internal error: {error}", exc_info=True)
return {"error": "Internal server error"}, 500
セキュリティヘッダー
SECURITY_HEADERS = {
"Strict-Transport-Security": "max-age=31536000; includeSubDomains",
"X-Content-Type-Options": "nosniff",
"X-Frame-Options": "DENY",
"X-XSS-Protection": "1; mode=block",
"Content-Security-Policy": "default-src 'self'",
"Referrer-Policy": "strict-origin-when-cross-origin",
}
シークレット管理
環境変数
# ✅ Store secrets in environment
export DATABASE_URL="postgres://..."
export API_KEY="..."
export JWT_SECRET="..."
# ❌ NEVER commit secrets to code
DATABASE_URL = "postgres://user:password@host/db" # In code!
シークレットの要件
| シークレットの種類 | ローテーション期間 | 保存場所 |
|---|---|---|
| API キー | 90 日 | Vault/環境変数 |
| データベースのパスワード | 90 日 | Vault/環境変数 |
| JWT シークレット | 30 日 | Vault/環境変数 |
| 暗号化キー | 1 年 | HSM/KMS |
| ユーザーのパスワード | 漏えい時 | DB にハッシュ化して保存 |
.gitignore
# Secrets - NEVER commit
.env
.env.local
.env.*.local
*.pem
*.key
credentials.json
secrets.yaml
config/secrets/*
ファイルアップロードのセキュリティ
検証チェックリスト
def validate_upload(file) -> bool:
"""Validate uploaded file."""
# 1. Check file size
MAX_SIZE = 10 * 1024 * 1024 # 10MB
if file.size > MAX_SIZE:
raise ValueError("File too large")
# 2. Check extension against allowlist
ALLOWED_EXTENSIONS = {'.jpg', '.jpeg', '.png', '.pdf'}
ext = Path(file.name).suffix.lower()
if ext not in ALLOWED_EXTENSIONS:
raise ValueError("File type not allowed")
# 3. Verify magic bytes (don't trust extension)
magic_bytes = file.read(8)
file.seek(0)
if not is_valid_magic(magic_bytes, ext):
raise ValueError("File content doesn't match extension")
# 4. Generate safe filename
safe_name = generate_safe_filename(file.name)
# 5. Store outside web root
storage_path = UPLOAD_DIR / safe_name # Not in /public!
return True
安全なファイル名の生成
import uuid
import re
from pathlib import Path
def generate_safe_filename(original: str) -> str:
"""Generate safe filename, preserving extension."""
ext = Path(original).suffix.lower()
# Use UUID, not original filename
return f"{uuid.uuid4()}{ext}"
ロギングとモニタリング
ログに記録する内容
# ✅ Log these security events
SECURITY_EVENTS = [
"login_success",
"login_failure",
"logout",
"password_change",
"password_reset_request",
"permission_denied",
"invalid_token",
"rate_limit_exceeded",
"suspicious_input",
"admin_action",
]
ログに記録しない内容
# ❌ NEVER log these
NEVER_LOG = [
"password",
"password_hash",
"credit_card",
"ssn",
"api_key",
"session_token",
"jwt_token",
"private_key",
]
構造化されたセキュリティログ
def log_security_event(event_type: str, user_id: str, details: dict):
"""Log security event with context."""
logger.info("security_event", extra={
"event_type": event_type,
"user_id": user_id,
"timestamp": datetime.utcnow().isoformat(),
"ip_address": get_client_ip(),
"user_agent": get_user_agent(),
**sanitize_details(details),
})
ビルダーチェックリスト
ユーザーデータ、認証、または外部入力を扱うコードを書く前に、これらの制約に照らして計画を確認してください。ビルダーはコードを書く前にこのセクションを読み、リファクタリング担当者は「適用されるルール」表と詳細な説明を使用します。
リリース前に毎回確認すること
- コードやバージョン管理システムにシークレットが含まれていない
- すべてのユーザー入力が検証およびサニタイズされている
- SQL クエリでパラメーター化されたステートメントを使用している
- 保護対象のすべてのエンドポイントで認証を実施している
- すべてのリソースに対して認可チェックを実施している
- HTTPS を強制している(HTTP は使用しない)
- セキュリティヘッダーを設定している
- レート制限を導入している
- エラーメッセージから機密情報が漏えいしない
- 依存関係を更新し、監査している
- ファイルアップロードを検証し、安全に保存している
- ログにセキュリティイベントを記録している
- CORS を厳格に設定している
コードレビューで重点的に確認するセキュリティ項目
- すべての入力箇所で入力検証を行っている
- XSS 防止のために出力をエンコードしている
- アクセス制御チェックを実施している
- ハードコードされたシークレットがない
- 適切なエラー処理を行っている(スタックトレースを表示しない)
- 安全なデフォルト設定を使用している
- サードパーティライブラリが必要であり、信頼できる
適用されるルール
これらのルールは check.js (clean-team) によって決定論的にチェックされます。これらの基準を更新する場合は、対応する check.js のルールも同様に更新してください。その逆も同様です。
| ルール ID | 重大度 | チェック内容 |
|---|---|---|
no-document-write | エラー | document.write() の使用(DOM インジェクションのリスク) |
no-hardcoded-secrets | エラー | 文字列リテラル内の API キー、トークン、パスワード |
no-innerHTML | 警告 | .innerHTML の代入(XSS のリスク) |
参考資料
references/owasp-top-10.md— OWASP の脆弱性に関する詳細ガイドreferences/input-validation.md— 入力検証パターンの完全版references/auth-patterns.md— 認証および認可のパターン
アセット
assets/security-checklist.md— リリース前のセキュリティチェックリストassets/threat-model-template.md— 脅威モデリングのテンプレート
スクリプト
scripts/scan_secrets.py— 誤ってコミットされたシークレットがないかコードをスキャンしますscripts/check_dependencies.py— 既知の脆弱性がないか依存関係をチェックします