security-and-hardening
ユーザーからの入力、認証、データ保管、外部連携を扱う機能や、信頼できないデータを受け取る、ユーザーセッションを管理する、第三者サービスと連携する機能を構築する際に、セキュリティを強化し安全性を高めるSkill。
📜 元の英語説明(参考)
Use when handling user input, authentication, data storage, or external integrations. Use when building any feature that accepts untrusted data, manages user sessions, or interacts with third-party services.
🇯🇵 日本人クリエイター向け解説
ユーザーからの入力、認証、データ保管、外部連携を扱う機能や、信頼できないデータを受け取る、ユーザーセッションを管理する、第三者サービスと連携する機能を構築する際に、セキュリティを強化し安全性を高めるSkill。
※ jpskill.com 編集部が日本のビジネス現場向けに補足した解説です。Skill本体の挙動とは独立した参考情報です。
下記のコマンドをコピーしてターミナル(Mac/Linux)または PowerShell(Windows)に貼り付けてください。 ダウンロード → 解凍 → 配置まで全自動。
mkdir -p ~/.claude/skills && cd ~/.claude/skills && curl -L -o security-and-hardening.zip https://jpskill.com/download/9588.zip && unzip -o security-and-hardening.zip && rm security-and-hardening.zip
$d = "$env:USERPROFILE\.claude\skills"; ni -Force -ItemType Directory $d | Out-Null; iwr https://jpskill.com/download/9588.zip -OutFile "$d\security-and-hardening.zip"; Expand-Archive "$d\security-and-hardening.zip" -DestinationPath $d -Force; ri "$d\security-and-hardening.zip"
完了後、Claude Code を再起動 → 普通に「動画プロンプト作って」のように話しかけるだけで自動発動します。
💾 手動でダウンロードしたい(コマンドが難しい人向け)
- 1. 下の青いボタンを押して
security-and-hardening.zipをダウンロード - 2. ZIPファイルをダブルクリックで解凍 →
security-and-hardeningフォルダができる - 3. そのフォルダを
C:\Users\あなたの名前\.claude\skills\(Win)または~/.claude/skills/(Mac)へ移動 - 4. Claude Code を再起動
⚠️ ダウンロード・利用は自己責任でお願いします。当サイトは内容・動作・安全性について責任を負いません。
🎯 このSkillでできること
下記の説明文を読むと、このSkillがあなたに何をしてくれるかが分かります。Claudeにこの分野の依頼をすると、自動で発動します。
📦 インストール方法 (3ステップ)
- 1. 上の「ダウンロード」ボタンを押して .skill ファイルを取得
- 2. ファイル名の拡張子を .skill から .zip に変えて展開(macは自動展開可)
- 3. 展開してできたフォルダを、ホームフォルダの
.claude/skills/に置く- · macOS / Linux:
~/.claude/skills/ - · Windows:
%USERPROFILE%\.claude\skills\
- · macOS / Linux:
Claude Code を再起動すれば完了。「このSkillを使って…」と話しかけなくても、関連する依頼で自動的に呼び出されます。
詳しい使い方ガイドを見る →- 最終更新
- 2026-05-18
- 取得日時
- 2026-05-18
- 同梱ファイル
- 1
📖 Skill本文(日本語訳)
※ 原文(英語/中国語)を Gemini で日本語化したものです。Claude 自身は原文を読みます。誤訳がある場合は原文をご確認ください。
セキュリティと強化
概要
Webアプリケーションのためのセキュリティを最優先とする開発プラクティスです。すべての外部入力を敵対的なものとして、すべての秘密を神聖なものとして、そしてすべての認可チェックを必須のものとして扱ってください。セキュリティはフェーズではありません。ユーザーデータ、認証、または外部システムに触れるすべてのコード行に対する制約です。
使用するタイミング
- ユーザー入力を受け入れるものを構築する場合
- 認証または認可を実装する場合
- 機密データを保存または送信する場合
- 外部APIまたはサービスと統合する場合
- ファイルアップロード、Webhook、またはコールバックを追加する場合
- 支払いまたはPIIデータを処理する場合
3層境界システム
常に実行する(例外なし)
- システム境界(APIルート、フォームハンドラー)ですべての外部入力を検証する
- すべてのデータベースクエリをパラメーター化する — ユーザー入力をSQLに連結しない
- XSSを防ぐために出力をエンコードする(フレームワークの自動エスケープを使用し、バイパスしない)
- すべての外部通信にHTTPSを使用する
- bcrypt/scrypt/argon2でパスワードをハッシュ化する(プレーンテキストで保存しない)
- セキュリティヘッダーを設定する(CSP、HSTS、X-Frame-Options、X-Content-Type-Options)
- セッションにはhttpOnly、secure、sameSiteのCookieを使用する
- リリース前に毎回
npm auditを実行する(または同等のもの)
最初に確認する(人間の承認が必要)
- 新しい認証フローの追加または認証ロジックの変更
- 新しいカテゴリの機密データ(PII、支払い情報)の保存
- 新しい外部サービス統合の追加
- CORS構成の変更
- ファイルアップロードハンドラーの追加
- レート制限またはスロットリングの変更
- 昇格された権限またはロールの付与
絶対にしないこと
- バージョン管理に秘密をコミットしない(APIキー、パスワード、トークン)
- 機密データをログに記録しない(パスワード、トークン、クレジットカード番号全体)
- セキュリティ境界としてクライアント側の検証を信頼しない
- 利便性のためにセキュリティヘッダーを無効にしない
- ユーザーが提供したデータで
eval()またはinnerHTMLを使用しない - クライアントがアクセス可能なストレージにセッションを保存しない(認証トークン用のlocalStorage)
- スタックトレースまたは内部エラーの詳細をユーザーに公開しない
OWASP Top 10 対策
1. インジェクション (SQL, NoSQL, OS Command)
// BAD: 文字列連結によるSQLインジェクション
const query = `SELECT * FROM users WHERE id = '${userId}'`;
// GOOD: パラメーター化されたクエリ
const user = await db.query('SELECT * FROM users WHERE id = $1', [userId]);
// GOOD: パラメーター化された入力を持つORM
const user = await prisma.user.findUnique({ where: { id: userId } });
2. 認証の不備
// パスワードのハッシュ化
import { hash, compare } from 'bcrypt';
const SALT_ROUNDS = 12;
const hashedPassword = await hash(plaintext, SALT_ROUNDS);
const isValid = await compare(plaintext, hashedPassword);
// セッション管理
app.use(session({
secret: process.env.SESSION_SECRET, // コードではなく環境から
resave: false,
saveUninitialized: false,
cookie: {
httpOnly: true, // JavaScript経由でアクセス不可
secure: true, // HTTPSのみ
sameSite: 'lax', // CSRF保護
maxAge: 24 * 60 * 60 * 1000, // 24時間
},
}));
3. クロスサイトスクリプティング (XSS)
// BAD: ユーザー入力をHTMLとしてレンダリング
element.innerHTML = userInput;
// GOOD: フレームワークの自動エスケープを使用する (Reactはデフォルトでこれを行う)
return <div>{userInput}</div>;
// HTMLをレンダリングする必要がある場合は、最初にサニタイズする
import DOMPurify from 'dompurify';
const clean = DOMPurify.sanitize(userInput);
4. 不適切なアクセス制御
// 認証だけでなく、常に認可を確認する
app.patch('/api/tasks/:id', authenticate, async (req, res) => {
const task = await taskService.findById(req.params.id);
// 認証されたユーザーがこのリソースを所有していることを確認する
if (task.ownerId !== req.user.id) {
return res.status(403).json({
error: { code: 'FORBIDDEN', message: 'Not authorized to modify this task' }
});
}
// 更新を続行する
const updated = await taskService.update(req.params.id, req.body);
return res.json(updated);
});
5. セキュリティ設定の誤り
// セキュリティヘッダー (Expressにはhelmetを使用)
import helmet from 'helmet';
app.use(helmet());
// コンテンツセキュリティポリシー
app.use(helmet.contentSecurityPolicy({
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'"],
styleSrc: ["'self'", "'unsafe-inline'"], // 可能であれば厳しくする
imgSrc: ["'self'", 'data:', 'https:'],
connectSrc: ["'self'"],
},
}));
// CORS — 既知のオリジンに制限する
app.use(cors({
origin: process.env.ALLOWED_ORIGINS?.split(',') || 'http://localhost:3000',
credentials: true,
}));
6. 機密データの露出
// APIレスポンスで機密フィールドを返さない
function sanitizeUser(user: UserRecord): PublicUser {
const { passwordHash, resetToken, ...publicFields } = user;
return publicFields;
}
// シークレットには環境変数を使用する
const API_KEY = process.env.STRIPE_API_KEY;
if (!API_KEY) throw new Error('STRIPE_API_KEY not configured');
入力検証パターン
境界でのスキーマ検証
import { z } from 'zod';
const CreateTaskSchema = z.object({
title: z.string().min(1).max(200).trim(),
description: z.string().max(2000).optional(),
priority: z.enum(['low', 'medium', 'high']).default('medium'),
dueDate: z.string().datetime().optional(),
});
// ルートハンドラーで検証する
app.post('/api/tasks', async (req, res) => {
const result = CreateTaskSchema.safeParse(req.body);
if (!result.success) {
return res.status(422).json({
error: {
code: 'VALIDATION_ERROR',
message: 'Invalid input',
details: result.error.flatten(),
},
});
}
// result.dataは型付けされ、検証済み
const task = await taskService.create(result.data);
return res.status(201).json(task);
});
ファイルアップロードの安全性
// ファイルの種類とサイズを制限する
const ALLOWED_TYPES = ['image/jpeg', 'image/png', 'image/webp'];
const MAX_SIZE = 5 * 102 📜 原文 SKILL.md(Claudeが読む英語/中国語)を展開
Security and Hardening
Overview
Security-first development practices for web applications. Treat every external input as hostile, every secret as sacred, and every authorization check as mandatory. Security isn't a phase — it's a constraint on every line of code that touches user data, authentication, or external systems.
When to Use
- Building anything that accepts user input
- Implementing authentication or authorization
- Storing or transmitting sensitive data
- Integrating with external APIs or services
- Adding file uploads, webhooks, or callbacks
- Handling payment or PII data
The Three-Tier Boundary System
Always Do (No Exceptions)
- Validate all external input at the system boundary (API routes, form handlers)
- Parameterize all database queries — never concatenate user input into SQL
- Encode output to prevent XSS (use framework auto-escaping, don't bypass it)
- Use HTTPS for all external communication
- Hash passwords with bcrypt/scrypt/argon2 (never store plaintext)
- Set security headers (CSP, HSTS, X-Frame-Options, X-Content-Type-Options)
- Use httpOnly, secure, sameSite cookies for sessions
- Run
npm audit(or equivalent) before every release
Ask First (Requires Human Approval)
- Adding new authentication flows or changing auth logic
- Storing new categories of sensitive data (PII, payment info)
- Adding new external service integrations
- Changing CORS configuration
- Adding file upload handlers
- Modifying rate limiting or throttling
- Granting elevated permissions or roles
Never Do
- Never commit secrets to version control (API keys, passwords, tokens)
- Never log sensitive data (passwords, tokens, full credit card numbers)
- Never trust client-side validation as a security boundary
- Never disable security headers for convenience
- Never use
eval()orinnerHTMLwith user-provided data - Never store sessions in client-accessible storage (localStorage for auth tokens)
- Never expose stack traces or internal error details to users
OWASP Top 10 Prevention
1. Injection (SQL, NoSQL, OS Command)
// BAD: SQL injection via string concatenation
const query = `SELECT * FROM users WHERE id = '${userId}'`;
// GOOD: Parameterized query
const user = await db.query('SELECT * FROM users WHERE id = $1', [userId]);
// GOOD: ORM with parameterized input
const user = await prisma.user.findUnique({ where: { id: userId } });
2. Broken Authentication
// Password hashing
import { hash, compare } from 'bcrypt';
const SALT_ROUNDS = 12;
const hashedPassword = await hash(plaintext, SALT_ROUNDS);
const isValid = await compare(plaintext, hashedPassword);
// Session management
app.use(session({
secret: process.env.SESSION_SECRET, // From environment, not code
resave: false,
saveUninitialized: false,
cookie: {
httpOnly: true, // Not accessible via JavaScript
secure: true, // HTTPS only
sameSite: 'lax', // CSRF protection
maxAge: 24 * 60 * 60 * 1000, // 24 hours
},
}));
3. Cross-Site Scripting (XSS)
// BAD: Rendering user input as HTML
element.innerHTML = userInput;
// GOOD: Use framework auto-escaping (React does this by default)
return <div>{userInput}</div>;
// If you MUST render HTML, sanitize first
import DOMPurify from 'dompurify';
const clean = DOMPurify.sanitize(userInput);
4. Broken Access Control
// Always check authorization, not just authentication
app.patch('/api/tasks/:id', authenticate, async (req, res) => {
const task = await taskService.findById(req.params.id);
// Check that the authenticated user owns this resource
if (task.ownerId !== req.user.id) {
return res.status(403).json({
error: { code: 'FORBIDDEN', message: 'Not authorized to modify this task' }
});
}
// Proceed with update
const updated = await taskService.update(req.params.id, req.body);
return res.json(updated);
});
5. Security Misconfiguration
// Security headers (use helmet for Express)
import helmet from 'helmet';
app.use(helmet());
// Content Security Policy
app.use(helmet.contentSecurityPolicy({
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'"],
styleSrc: ["'self'", "'unsafe-inline'"], // Tighten if possible
imgSrc: ["'self'", 'data:', 'https:'],
connectSrc: ["'self'"],
},
}));
// CORS — restrict to known origins
app.use(cors({
origin: process.env.ALLOWED_ORIGINS?.split(',') || 'http://localhost:3000',
credentials: true,
}));
6. Sensitive Data Exposure
// Never return sensitive fields in API responses
function sanitizeUser(user: UserRecord): PublicUser {
const { passwordHash, resetToken, ...publicFields } = user;
return publicFields;
}
// Use environment variables for secrets
const API_KEY = process.env.STRIPE_API_KEY;
if (!API_KEY) throw new Error('STRIPE_API_KEY not configured');
Input Validation Patterns
Schema Validation at Boundaries
import { z } from 'zod';
const CreateTaskSchema = z.object({
title: z.string().min(1).max(200).trim(),
description: z.string().max(2000).optional(),
priority: z.enum(['low', 'medium', 'high']).default('medium'),
dueDate: z.string().datetime().optional(),
});
// Validate at the route handler
app.post('/api/tasks', async (req, res) => {
const result = CreateTaskSchema.safeParse(req.body);
if (!result.success) {
return res.status(422).json({
error: {
code: 'VALIDATION_ERROR',
message: 'Invalid input',
details: result.error.flatten(),
},
});
}
// result.data is now typed and validated
const task = await taskService.create(result.data);
return res.status(201).json(task);
});
File Upload Safety
// Restrict file types and sizes
const ALLOWED_TYPES = ['image/jpeg', 'image/png', 'image/webp'];
const MAX_SIZE = 5 * 1024 * 1024; // 5MB
function validateUpload(file: UploadedFile) {
if (!ALLOWED_TYPES.includes(file.mimetype)) {
throw new ValidationError('File type not allowed');
}
if (file.size > MAX_SIZE) {
throw new ValidationError('File too large (max 5MB)');
}
// Don't trust the file extension — check magic bytes if critical
}
Rate Limiting
import rateLimit from 'express-rate-limit';
// General API rate limit
app.use('/api/', rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // 100 requests per window
standardHeaders: true,
legacyHeaders: false,
}));
// Stricter limit for auth endpoints
app.use('/api/auth/', rateLimit({
windowMs: 15 * 60 * 1000,
max: 10, // 10 attempts per 15 minutes
}));
Secrets Management
.env files:
├── .env.example → Committed (template with placeholder values)
├── .env → NOT committed (contains real secrets)
└── .env.local → NOT committed (local overrides)
.gitignore must include:
.env
.env.local
.env.*.local
*.pem
*.key
Always check before committing:
# Check for accidentally staged secrets
git diff --cached | grep -i "password\|secret\|api_key\|token"
Security Review Checklist
### Authentication
- [ ] Passwords hashed with bcrypt/scrypt/argon2 (salt rounds ≥ 12)
- [ ] Session tokens are httpOnly, secure, sameSite
- [ ] Login has rate limiting
- [ ] Password reset tokens expire
### Authorization
- [ ] Every endpoint checks user permissions
- [ ] Users can only access their own resources
- [ ] Admin actions require admin role verification
### Input
- [ ] All user input validated at the boundary
- [ ] SQL queries are parameterized
- [ ] HTML output is encoded/escaped
### Data
- [ ] No secrets in code or version control
- [ ] Sensitive fields excluded from API responses
- [ ] PII encrypted at rest (if applicable)
### Infrastructure
- [ ] Security headers configured (CSP, HSTS, etc.)
- [ ] CORS restricted to known origins
- [ ] Dependencies audited for vulnerabilities
- [ ] Error messages don't expose internals
Common Rationalizations
| Rationalization | Reality |
|---|---|
| "This is an internal tool, security doesn't matter" | Internal tools get compromised. Attackers target the weakest link. |
| "We'll add security later" | Security retrofitting is 10x harder than building it in. Add it now. |
| "No one would try to exploit this" | Automated scanners will find it. Security by obscurity is not security. |
| "The framework handles security" | Frameworks provide tools, not guarantees. You still need to use them correctly. |
| "It's just a prototype" | Prototypes become production. Security habits from day one. |
Red Flags
- User input passed directly to database queries, shell commands, or HTML rendering
- Secrets in source code or commit history
- API endpoints without authentication or authorization checks
- Missing CORS configuration or wildcard (
*) origins - No rate limiting on authentication endpoints
- Stack traces or internal errors exposed to users
- Dependencies with known critical vulnerabilities
Verification
After implementing security-relevant code:
- [ ]
npm auditshows no critical or high vulnerabilities - [ ] No secrets in source code or git history
- [ ] All user input validated at system boundaries
- [ ] Authentication and authorization checked on every protected endpoint
- [ ] Security headers present in response (check with browser DevTools)
- [ ] Error responses don't expose internal details
- [ ] Rate limiting active on auth endpoints