mcpField
S
security-testing-verification

security-testing-verification

github.com/harperaaセキュリティ19197 回インストール45 週間前に更新
リポジトリ

security-testing-verification について

security-testing-verification は、プロジェクトのセキュリティ機能をテスト・検証するためのガイドです。スクリプト、リクエスト例、デプロイ用チェックリストを備え、レート制限、CSRF、入力検証、ヘッダー、認証などを扱います。

主な機能

  1. 01レート制限とリセット動作をテスト
  2. 02CSRF トークンの処理を検証
  3. 03入力検証とサニタイズを確認
  4. 04セキュリティヘッダーと認証を点検
  5. 05依存関係監査とセキュリティスクリプトを実行

活用シーン

  1. 01開発中に API 保護を検証
  2. 02認証と認可の動作をテスト
  3. 03デプロイ前にセキュリティ項目を確認

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/harperaa/secure-claude-skills --skill security-testing-verification

インストール後、Claude Code で/security-testing-verificationを入力して読み込まれたか確認します。

詳細

セキュリティテストと検証

組み込みのセキュリティテスト

このプロジェクトには、すべてのセキュリティ機能を対象とする自動テストと検証スクリプトが含まれています。

レート制限のテスト

自動テストスクリプト
# Run the provided test script
node scripts/test-rate-limit.js

テスト内容:

  • レート制限対象のエンドポイントに連続して10件リクエストを送信します
  • 最初の5件が成功することを確認します(HTTP 200)
  • 6〜10件目のリクエストがブロックされることを確認します(HTTP 429)
  • 60秒後にレート制限がリセットされることをテストします

想定される出力:

Testing Rate Limiting (5 requests/minute per IP)
Request  1: ✓ 200 - Success
Request  2: ✓ 200 - Success
Request  3: ✓ 200 - Success
Request  4: ✓ 200 - Success
Request  5: ✓ 200 - Success
Request  6: ✗ 429 - Too many requests
Request  7: ✗ 429 - Too many requests
Request  8: ✗ 429 - Too many requests
Request  9: ✗ 429 - Too many requests
Request 10: ✗ 429 - Too many requests

✓ Rate limiting is working correctly!
手動テスト
# Test rate limiting manually
for i in {1..10}; do
  echo "Request $i:"
  curl -s -o /dev/null -w "%{http_code}\n" \
    http://localhost:3000/api/test-rate-limit
  sleep 0.1
done

# Expected:
# Requests 1-5: 200
# Requests 6-10: 429
ウィンドウ経過後のリセットをテスト
# Make 5 requests
for i in {1..5}; do
  curl http://localhost:3000/api/test-rate-limit
done

# Wait 61 seconds (rate limit window = 60 seconds)
sleep 61

# Try again - should succeed
curl http://localhost:3000/api/test-rate-limit

# Expected: 200 OK (limit reset)

CSRF 保護のテスト

テスト 1:トークンなしのリクエスト(失敗するはずです)
curl -X POST http://localhost:3000/api/example-protected \
  -H "Content-Type: application/json" \
  -d '{"title": "test"}'

# Expected: 403 Forbidden
# {
#   "error": "CSRF token missing"
# }
テスト 2:有効なトークンを含むリクエスト(成功するはずです)
# Step 1: Get CSRF token
TOKEN=$(curl -s http://localhost:3000/api/csrf \
  -c cookies.txt | jq -r '.csrfToken')

# Step 2: Use token in request
curl -X POST http://localhost:3000/api/example-protected \
  -b cookies.txt \
  -H "Content-Type: application/json" \
  -H "X-CSRF-Token: $TOKEN" \
  -d '{"title": "test"}'

# Expected: 200 OK
テスト 3:トークンの再利用(失敗するはずです)
# Get token
TOKEN=$(curl -s http://localhost:3000/api/csrf \
  -c cookies.txt | jq -r '.csrfToken')

# Use once (succeeds)
curl -X POST http://localhost:3000/api/example-protected \
  -b cookies.txt \
  -H "X-CSRF-Token: $TOKEN" \
  -d '{"title": "test"}'

# Try to reuse same token (should fail)
curl -X POST http://localhost:3000/api/example-protected \
  -b cookies.txt \
  -H "X-CSRF-Token: $TOKEN" \
  -d '{"title": "test2"}'

# Expected: 403 Forbidden - Token already used
テスト 4:無効なトークン(失敗するはずです)
curl -X POST http://localhost:3000/api/example-protected \
  -H "Content-Type: application/json" \
  -H "X-CSRF-Token: fake-token-12345" \
  -d '{"title": "test"}'

# Expected: 403 Forbidden
# {
#   "error": "CSRF token invalid"
# }

入力検証のテスト

XSS サニタイズのテスト
# Test script tags removal
curl -X POST http://localhost:3000/api/example-protected \
  -H "Content-Type: application/json" \
  -H "X-CSRF-Token: <get-token-first>" \
  -d '{"title": "<script>alert(1)</script>"}'

# Expected: 200 OK
# Title sanitized to: "alert(1)"
# < and > removed
長さ検証のテスト
# Test too-long input
curl -X POST http://localhost:3000/api/example-protected \
  -H "Content-Type: application/json" \
  -H "X-CSRF-Token: <token>" \
  -d "{\"title\": \"$(printf 'A%.0s' {1..200})\"}"

# Expected: 400 Bad Request
# {
#   "error": "Validation failed",
#   "details": {
#     "title": "String must contain at most 100 character(s)"
#   }
# }
メールアドレス検証のテスト
curl -X POST http://localhost:3000/api/contact \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Test User",
    "email": "not-an-email",
    "subject": "Test",
    "message": "Test message"
  }'

# Expected: 400 Bad Request
# {
#   "error": "Validation failed",
#   "details": {
#     "email": "Invalid email"
#   }
# }
必須フィールドのテスト
curl -X POST http://localhost:3000/api/contact \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Test User"
  }'

# Expected: 400 Bad Request with missing field errors

セキュリティヘッダーのテスト

すべてのヘッダーをテスト
curl -I http://localhost:3000

# Expected headers:
# Content-Security-Policy: default-src 'self'; ...
# X-Frame-Options: DENY
# X-Content-Type-Options: nosniff
# (HSTS only in production)
CSP のテスト
# Check CSP includes required domains
curl -I http://localhost:3000 | grep "Content-Security-Policy"

# Should include:
# - script-src with Clerk domain
# - connect-src with Convex domain
# - frame-src with Stripe domain
HSTS のテスト(本番環境のみ)
# In production environment
curl -I https://yourapp.com | grep "Strict-Transport-Security"

# Should return:
# Strict-Transport-Security: max-age=31536000; includeSubDomains
保護されたルートのヘッダーをテスト
curl -I http://localhost:3000/dashboard

# Should include:
# X-Robots-Tag: noindex, nofollow

認証のテスト

未認証アクセスのテスト
# Try to access protected API without auth
curl http://localhost:3000/api/protected-endpoint

# Expected: 401 Unauthorized
# {
#   "error": "Unauthorized",
#   "message": "Authentication required"
# }
認証済みアクセスのテスト
# With valid Clerk session cookie
curl http://localhost:3000/api/protected-endpoint \
  -H "Cookie: __session=<clerk-session-token>"

# Expected: 200 OK (with authorized response)
認可のテスト(リソースの所有権)
# Try to access another user's resource
curl http://localhost:3000/api/posts/user-abc-post-123 \
  -H "Cookie: __session=<different-user-token>"

# Expected: 403 Forbidden
# {
#   "error": "Forbidden",
#   "message": "You do not have access to this resource"
# }
サブスクリプションによる制限のテスト
# Try premium feature with free account
curl http://localhost:3000/api/premium/generate \
  -H "Cookie: __session=<free-user-token>"

# Expected: 403 Forbidden
# {
#   "error": "Forbidden",
#   "message": "Premium subscription required"
# }

エラーハンドリングのテスト

本番環境のエラーメッセージをテスト
# Set NODE_ENV=production temporarily
export NODE_ENV=production

# Trigger error in API
curl http://localhost:3000/api/error-test

# Expected: Generic message (no stack trace)
# {
#   "error": "Internal server error",
#   "message": "An unexpected error occurred"
# }
開発環境のエラーメッセージをテスト
# In development (NODE_ENV=development)
curl http://localhost:3000/api/error-test

# Expected: Detailed error with stack trace
# {
#   "error": "Internal server error",
#   "message": "Specific error message",
#   "stack": "Error: ...\n    at ...",
#   "context": "error-test"
# }

依存関係のセキュリティテスト

npm Audit の実行
# Check for vulnerabilities
npm audit

# Expected: 0 vulnerabilities
# found 0 vulnerabilities
本番環境の Audit の実行
# Only check production dependencies
npm audit --production

# Expected: 0 vulnerabilities
古くなったパッケージを確認
npm outdated

# Expected: All packages up-to-date
# (or list of safe minor/patch updates available)
セキュリティチェック スクリプトの実行
bash scripts/security-check.sh

# Expected:
# - 0 vulnerabilities
# - Minimal outdated packages
# - Fix commands if needed

オンラインセキュリティテストツール

セキュリティヘッダースキャナー

ツール: https://securityheaders.com/

使用方法:

  1. アプリをデプロイします
  2. Security Headers スキャナーに URL を入力します
  3. A+ 評価を確認します

チェック項目:

  • Content-Security-Policy
  • X-Frame-Options
  • X-Content-Type-Options
  • Strict-Transport-Security
  • Referrer-Policy
  • Permissions-Policy
Mozilla Observatory

ツール: https://observatory.mozilla.org/

使用方法:

  1. デプロイ済みの URL を入力します
  2. スキャンを実行します
  3. スコアを確認します(A+ を目指してください)

チェック項目:

  • セキュリティヘッダー
  • Cookie のセキュリティ
  • HTTPS の設定
  • サブリソースの整合性
  • Content Security Policy
SSL Labs

ツール: https://www.ssllabs.com/ssltest/

使用方法:

  1. ドメインを入力します
  2. スキャンが完了するまで待ちます(約2分かかります)
  3. A+ 評価を確認します

チェック項目:

  • SSL/TLS の設定
  • 証明書の有効性
  • プロトコルのサポート
  • 暗号スイートの強度
  • HSTS の設定

デプロイ前のセキュリティチェックリスト

本番環境にデプロイする前に、毎回このチェックリストを確認してください。

環境と設定
  • すべての環境変数が本番環境に設定されている
  • CSRF_SECRET が生成され、設定されている(32バイト以上)
  • SESSION_SECRET が生成され、設定されている(32バイト以上)
  • Clerk の本番環境用キーが設定されている
  • Stripe の Live モード用キーが設定されている(決済を使用する場合)
  • .env.local が git にコミットされていない
依存関係
  • npm audit --production を実行済み — 脆弱性 0 件
  • npm outdated を実行済み — 重大な更新がないか確認
  • package-lock.json がコミットされている
  • Next.js が最新の安定バージョンである
セキュリティ機能
  • CSRF 保護をテスト済み(上記のテストを参照)
  • レート制限をテスト済み(上記のテストを参照)
  • 入力検証をテスト済み(上記のテストを参照)
  • セキュリティヘッダーを確認済み(securityheaders.com)
  • 本番環境で HSTS が有効になっている
  • 本番環境のエラーメッセージが一般的な内容になっている
認証と認可
  • 保護されたルートで認証が必須になっている
  • アクセス前にリソースの所有権が確認されている
  • プレミアム機能でサブスクリプションの状態が検証されている
  • Webhook の署名が検証されている(Clerk、Stripe)
  • セッションの有効期限切れが適切に処理されている
API セキュリティ
  • すべての POST/PUT/DELETE ルートで CSRF 保護を実施している
  • すべての公開エンドポイントでレート制限を実施している
  • すべてのユーザー入力を Zod で検証している
  • すべてのエラーをエラーハンドラーユーティリティで処理している
  • ログに機密データを含めていない
  • コードにシークレットをハードコードしていない
決済セキュリティ(該当する場合)
  • Clerk Billing と Stripe を使用している(カード情報を直接扱っていない)
  • Webhook を検証している(Svix 署名)
  • サブスクリプションのステータスをサーバー側で確認している
  • 本番環境でテストモードを無効にしている
テスト
  • レート制限テストを実行する:node scripts/test-rate-limit.js
  • CSRF 保護を手動でテストする
  • 悪意のある入力で入力検証をテストする
  • セキュリティヘッダーを確認する:curl -I https://yourapp.com
  • 認証フローをテストする
  • 本番モードでエラー処理をテストする
モニタリング
  • エラーログを設定している(Vercel ログ)
  • 認証失敗の試行を追跡している(Clerk ダッシュボード)
  • GitHub Dependabot のアラートを有効にしている
  • セキュリティヘッダーをモニタリングしている(自動チェック)

自動テストスクリプト

security-test.sh

包括的なテストスクリプトを作成します:

#!/bin/bash

echo "================================="
echo "Security Testing Suite"
echo "================================="
echo ""

# Color codes
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color

# Test counter
PASSED=0
FAILED=0

# Function to run test
run_test() {
    local test_name=$1
    local command=$2
    local expected=$3

    echo -n "Testing $test_name... "

    result=$(eval $command 2>&1)

    if echo "$result" | grep -q "$expected"; then
        echo -e "${GREEN}✓ PASS${NC}"
        ((PASSED++))
    else
        echo -e "${RED}✗ FAIL${NC}"
        echo "  Expected: $expected"
        echo "  Got: $result"
        ((FAILED++))
    fi
}

echo "=== Dependency Security ==="
run_test "npm audit" "npm audit --production" "found 0 vulnerabilities"

echo ""
echo "=== Rate Limiting ==="
echo "Running rate limit test script..."
node scripts/test-rate-limit.js

echo ""
echo "=== Security Headers ==="
run_test "X-Frame-Options" "curl -I http://localhost:3000" "X-Frame-Options: DENY"
run_test "X-Content-Type-Options" "curl -I http://localhost:3000" "X-Content-Type-Options: nosniff"
run_test "Content-Security-Policy" "curl -I http://localhost:3000" "Content-Security-Policy"

echo ""
echo "================================="
echo "Tests Passed: $PASSED"
echo "Tests Failed: $FAILED"
echo "================================="

if [ $FAILED -eq 0 ]; then
    echo -e "${GREEN}All tests passed!${NC}"
    exit 0
else
    echo -e "${RED}Some tests failed!${NC}"
    exit 1
fi

実行方法:

bash scripts/security-test.sh

継続的なセキュリティテスト

CI/CD パイプラインに追加する
# .github/workflows/security.yml
name: Security Tests

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

jobs:
  security:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '18'

      - name: Install dependencies
        run: npm ci

      - name: Run npm audit
        run: npm audit --production

      - name: Check for outdated packages
        run: npm outdated || true

      - name: Build application
        run: npm run build

      - name: Start server (background)
        run: npm run dev &
        env:
          NODE_ENV: test

      - name: Wait for server
        run: npx wait-on http://localhost:3000

      - name: Run security tests
        run: bash scripts/security-test.sh

      - name: Stop server
        run: pkill -f "npm run dev"

手動ペネトレーションテスト

すべての入力フィールドで XSS をテストする
  1. すべての入力欄で次のペイロードを試します:

    <script>alert('XSS')</script>
    <img src=x onerror=alert('XSS')>
    <svg onload=alert('XSS')>
    javascript:alert('XSS')
    "><script>alert('XSS')</script>
    
  2. すべてがサニタイズされることを確認します

SQL インジェクションをテストする
  1. 検索/クエリフィールドで次の内容を試します:

    ' OR '1'='1
    '; DROP TABLE users; --
    ' UNION SELECT * FROM users --
    
  2. 入力検証によってブロックまたはサニタイズされることを確認します

CSRF をテストする
  1. 悪意のある HTML ファイルを作成します:

    <form action="http://localhost:3000/api/delete-account" method="POST">
      <input type="hidden" name="confirm" value="yes" />
    </form>
    <script>document.forms[0].submit();</script>
    
  2. ログインした状態で開きます

  3. リクエストがブロックされることを確認します(403 Forbidden)

認可をテストする
  1. ユーザー A としてリソースを作成します
  2. ユーザー B としてアクセスまたは変更を試みます
  3. 403 Forbidden になることを確認します

デプロイ後にモニタリングする項目

毎日
  • エラー率(Vercel ダッシュボード)
  • 認証失敗の試行(Clerk ダッシュボード)
  • レート制限違反(ログで 429 レスポンスを確認)
毎週
  • npm audit --production を実行する
  • GitHub Dependabot のアラートを確認する
  • エラーログを確認し、パターンを把握する
毎月
  • セキュリティ監査を全面的に実施する
  • 依存関係を更新する
  • セキュリティテストスイートを再実行する
  • セキュリティヘッダーを確認する(securityheaders.com)

参考資料

次のステップ

  • 問題の修正:適切なセキュリティスキル(csrf-protection、rate-limiting など)を使用する
  • デプロイ:上記のデプロイ前チェックリストを完了する
  • モニタリング:CI/CD で自動セキュリティスキャンを設定する
  • 継続的な保守:毎月セキュリティ監査を実施する

コメント