security について
securityは、アプリケーションコードと依存関係のセキュリティレビューを支援する開発スキルです。監査コマンドや例、一般的なWebリスクを扱うチェックリストを備えています。
主な機能
- 01pnpm auditとSnykによる確認
- 02OWASP Top 10のレビュー指針
- 03Zodによる入力検証
- 04アクセス制御とインジェクション対策の例
- 05CORSとセキュリティヘッダーの指針
- 06コードとCIのセキュリティチェックリスト
活用シーン
- 01依存関係の既知の脆弱性を確認
- 02認可と入力処理をレビュー
- 03CORSとレスポンスヘッダーを強化
- 04CIにセキュリティチェックを追加
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ motormetrics/ motormetrics --skill securityインストール後、Claude Code で/securityを入力して読み込まれたか確認します。
詳細
セキュリティスキル
依存関係のスキャン
pnpm audit
# Run audit
pnpm audit
# Only high/critical
pnpm audit --audit-level=high
# Auto-fix
pnpm audit --fix
# JSON report
pnpm audit --json > audit.json
脆弱性の修正
直接依存関係: pnpm-workspace.yaml のカタログでバージョンを更新します
間接依存関係:
# Find dependency chain
pnpm why vulnerable-package
# Use overrides as last resort
# package.json
{
"pnpm": {
"overrides": {
"vulnerable-package": "^3.1.0"
}
}
}
Snyk
snyk auth # Authenticate
snyk test # Test for vulnerabilities
snyk monitor # Monitor for new vulnerabilities
snyk fix # Auto-fix
OWASP Top 10 のチェック
1. アクセス制御の不備
// ❌ No authorization
export async function deletePost(postId: string) {
await db.delete(posts).where(eq(posts.id, postId));
}
// ✅ With authorization
export async function deletePost(postId: string, userId: string) {
const post = await db.query.posts.findFirst({ where: eq(posts.id, postId) });
if (post.authorId !== userId) throw new Error("Unauthorized");
await db.delete(posts).where(eq(posts.id, postId));
}
2. インジェクションの防止
// ❌ SQL Injection
const query = `SELECT * FROM users WHERE id = ${userId}`;
// ✅ Parameterized query (Drizzle ORM)
const user = await db.query.users.findFirst({ where: eq(users.id, userId) });
3. XSS の防止
React はデフォルトでコンテンツをエスケープします。HTML をレンダリングする場合:
- レンダリング前に
sanitize-htmlライブラリでサニタイズします - 信頼できないコンテンツを直接レンダリングしないでください
4. レート制限
import { Ratelimit } from "@upstash/ratelimit";
import { redis } from "@sgcarstrends/utils";
const ratelimit = new Ratelimit({
redis,
limiter: Ratelimit.slidingWindow(5, "15 m"),
});
export async function login(email: string, password: string, ip: string) {
const { success } = await ratelimit.limit(ip);
if (!success) throw new Error("Too many login attempts");
return verifyCredentials(email, password);
}
5. パスワードのセキュリティ
import bcrypt from "bcrypt";
// ✅ Hash passwords
const hashedPassword = await bcrypt.hash(password, 10);
// ✅ Strong password validation
const passwordSchema = z.string()
.min(12)
.regex(/[A-Z]/, "Must contain uppercase")
.regex(/[a-z]/, "Must contain lowercase")
.regex(/[0-9]/, "Must contain number")
.regex(/[^A-Za-z0-9]/, "Must contain special character");
6. SSRF の防止
// ❌ SSRF vulnerability
export async function fetchUrl(url: string) {
return await fetch(url);
}
// ✅ Whitelist approach
const ALLOWED_DOMAINS = ["api.example.com", "data.gov.sg"];
export async function fetchUrl(url: string) {
const parsedUrl = new URL(url);
if (!ALLOWED_DOMAINS.includes(parsedUrl.hostname)) {
throw new Error("Domain not allowed");
}
return await fetch(url);
}
入力の検証
import { z } from "zod";
const userInputSchema = z.object({
name: z.string().min(1).max(100),
email: z.string().email(),
age: z.number().int().min(0).max(150),
});
export async function createUser(data: unknown) {
const validated = userInputSchema.parse(data);
// Now safe to use
}
CORS の設定
// ❌ Too permissive
app.use(cors({ origin: "*" }));
// ✅ Whitelist specific origins
app.use(cors({
origin: [
"https://sgcarstrends.com",
"https://staging.sgcarstrends.com",
process.env.NODE_ENV === "development" ? "http://localhost:3001" : "",
].filter(Boolean),
credentials: true,
}));
セキュリティヘッダー
// next.config.js
const securityHeaders = [
{ key: "Strict-Transport-Security", value: "max-age=63072000; includeSubDomains; preload" },
{ key: "X-Frame-Options", value: "SAMEORIGIN" },
{ key: "X-Content-Type-Options", value: "nosniff" },
{ key: "X-XSS-Protection", value: "1; mode=block" },
{ key: "Referrer-Policy", value: "origin-when-cross-origin" },
];
module.exports = {
async headers() {
return [{ source: "/:path*", headers: securityHeaders }];
},
};
環境変数
// ❌ Hardcoded secret
const apiKey = "sk_live_EXAMPLE_NOT_REAL";
// ✅ From environment with validation
import { z } from "zod";
const envSchema = z.object({
API_KEY: z.string().min(1),
DATABASE_URL: z.string().url(),
});
const env = envSchema.parse(process.env);
CI との統合
# .github/workflows/security.yml
name: Security Audit
on:
push:
branches: [main]
schedule:
- cron: '0 0 * * 1' # Weekly
jobs:
audit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v2
- run: pnpm install
- run: pnpm audit --audit-level=high
セキュリティチェックリスト
- すべてのユーザー入力を検証する(Zod スキーマ)
- SQL インジェクションを防止する(ORM を使用)
- XSS を防止する(React によるエスケープ、サニタイズ)
- 認証を正しく実装する
- 認可チェックを実施する
- パスワードをハッシュ化する(bcrypt/argon2)
- レート制限を設定する
- セキュリティヘッダーを設定する
- CORS を適切に設定する
- HTTPS を強制する
- 依存関係を監査する(pnpm audit)
- シークレットを環境変数に設定する
- エラーメッセージから情報が漏えいしないようにする
参考資料
- OWASP Top 10:https://owasp.org/www-project-top-ten
- pnpm Audit:https://pnpm.io/cli/audit
- Snyk:https://snyk.io
コメント
Markdown 対応