Claude Codeのエージェント機能を活用することで、開発中のコードに対して網羅的なセキュリティ監査を実施できます。この記事では、実際にNext.jsプロジェクトで検出・修正した脆弱性5つを具体例とともに解説します。
はじめに
Webアプリケーション開発では、機能実装に注力するあまりセキュリティ対策が後回しになることがあります。
「セキュリティレビューは後で」という気持ちで本番環境にデプロイしてしまった経験をお持ちではないでしょうか?
本記事では、Claude Codeのエージェント機能を使ったセキュリティ監査の実施方法と、実際のプロジェクトで発見された脆弱性5つの修正方法(diff付き)を紹介します。
記事の内容
- Claude Codeセキュリティ監査の実行手順
- 実例として検出された脆弱性5つと修正コード
- 開発ワークフローへの監査組み込み方法
Claude Codeによるセキュリティ監査の実行方法
1. 監査指示をエージェントに送る
Claude Codeのプロンプトで、以下のような監査項目を明示します。
このプロジェクトに対してセキュリティ監査を実施してください。
以下の項目を重点的にチェックしてください:
1. 全APIエンドポイントの認証・認可実装
2. 入力値バリデーションの完全性
3. XSS(クロスサイトスクリプティング)対策
4. CSRF(クロスサイトリクエストフォージェリ)対策
5. オープンリダイレクトの有無
6. APIキー・秘密情報の露出確認
7. セキュリティヘッダー設定
発見した脆弱性については、
重大度(Critical/High/Medium/Low)と修正コード案を提示してください。
2. 全ファイルの走査と監査レポート生成
Claude Codeはプロジェクト内のすべてのファイルを解析し、上記の観点から問題を抽出します。APIルート、ミドルウェア、フロントエンドコンポーネント全域を横断的にチェック可能です。
3. CLAUDE.mdにセキュリティチェックリストを記載
毎回同じプロンプトを入力するのは効率的ではないため、CLAUDE.mdにセキュリティ監査項目をまとめておくことをお勧めします。
### セキュリティ確認リスト(必須項目)
各タスク完了時に以下を確認してください:
1. クライアント側のコードにAPIキーが含まれていないか
2. サーバーサイド専用の機密情報がバンドルに含まれていないか
3. `.env`ファイルが`.gitignore`に記載されているか
4. 認証・課金処理のバイパスが防止されているか
5. 新規APIエンドポイントにレート制限が実装されているか
違反が見つかった場合は、修正を終えてから次のタスクに進むこと。
このようにしておくと、Claude Codeは機能追加時に自動的にセキュリティチェックを実施するようになります。
実際に検出された脆弱性5つ
以降は、Next.jsプロジェクトで実際にClaude Codeが発見した脆弱性と修正方法を紹介します。
脆弱性1: オープンリダイレクト(Critical)
説明: 認証後のリダイレクト先を指定するnextパラメータが検証されず、外部ドメインへのリダイレクトが発生する問題です。
ログイン後に前のページに戻す機能で、nextパラメータの値をそのまま使用していました。
/auth/callback?next=//evil.com
このようなURLでアクセスすると、ログイン後にevil.comにリダイレクトされます。フィッシング詐欺に悪用される可能性があります。
修正前:
// nextパラメータを未検証で使用
const next = searchParams.get("next") || "/";
redirect(next);
修正後:
// リダイレクト先を検証してから使用
const next = searchParams.get("next") || "/";
const isSafeRedirect =
next.startsWith("/") &&
!next.startsWith("//") &&
!next.includes("@");
redirect(isSafeRedirect ? next : "/");
//evil.comはプロトコル相対URLとしてブラウザが解釈するため、//で始まるパスを除外します。また//user@evil.com形式の攻撃も防ぐため@を含むURLも拒否しています。
脆弱性2: javascript: URLを使ったXSS(High)
説明: ユーザーが入力したURLをhref属性にそのまま設定し、javascript:スキームでのスクリプト実行が可能です。
例えば、外部サイトのURL入力欄に以下を入力するとスクリプトが実行されます。
javascript:alert(document.cookie)
修正前:
// ユーザー入力をhrefに直接指定
<a href={shop.onlineShopUrl} target="_blank" rel="noopener">
外部サイトを開く
</a>
修正後:
// httpまたはhttpsスキームのみ許可
const isValidUrl = (url: string): boolean => {
return /^https?:\/\//.test(url);
};
{isValidUrl(shop.onlineShopUrl) && (
<a href={shop.onlineShopUrl} target="_blank" rel="noopener noreferrer">
外部サイトを開く
</a>
)}
https://またはhttp://で始まるURLのみを許可し、それ以外はリンクをレンダリングしません。
脆弱性3: 入力値検証の不足(High)
説明: POSTおよびPUTエンドポイントでリクエストボディのサイズ上限がなく、参照IDの妥当性確認が実施されていません。
数MBのJSONを送信してデータベースを肥大化させたり、存在しないindustry_idで不整合データを作成できる状態でした。
修正前:
// リクエストボディを検証なしで使用
export async function POST(request: Request) {
const body = await request.json();
const { data } = await supabase
.from("sites")
.insert(body);
return NextResponse.json(data);
}
修正後:
// サイズ制限と参照IDの妥当性確認を追加
const MAX_BODY_SIZE = 50 * 1024; // 50KB
const VALID_INDUSTRY_IDS = [
"restaurant", "salon", "clinic", "gym", "realestate"
];
export async function POST(request: Request) {
// リクエストサイズの確認
const contentLength = request.headers.get("content-length");
if (contentLength && parseInt(contentLength) > MAX_BODY_SIZE) {
return NextResponse.json(
{ error: "リクエストサイズが大きすぎます" },
{ status: 413 }
);
}
const body = await request.json();
// IDの妥当性確認
if (!VALID_INDUSTRY_IDS.includes(body.industry_id)) {
return NextResponse.json(
{ error: "無効な業種IDが指定されました" },
{ status: 400 }
);
}
const { data } = await supabase
.from("sites")
.insert({
industry_id: body.industry_id,
template_id: body.template_id,
// 必要なフィールドのみ明示的に取得
});
return NextResponse.json(data);
}
脆弱性4: CSRF トークン不在(High)
説明: フォーム送信時にCSRFトークンの検証がなく、他サイトからのリクエストフォージェリが可能です。
修正前:
// CSRF対策なしのフォーム処理
export async function POST(request: Request) {
const body = await request.json();
await supabase.from("items").insert(body);
return NextResponse.json({ ok: true });
}
修正後:
// csrf パッケージを使用したトークン検証
import { csrf } from "@edge-csrf/nextjs";
const csrfProtect = csrf({
cookie: {
secure: process.env.NODE_ENV === "production",
},
});
export async function POST(request: Request) {
await csrfProtect(request);
const body = await request.json();
await supabase.from("items").insert(body);
return NextResponse.json({ ok: true });
}
脆弱性5: セキュリティヘッダー未設定(Medium)
説明: 重要なセキュリティヘッダー(Content-Security-Policy、X-Frame-Optionsなど)が設定されていません。
修正前:
// ヘッダー設定なし
export async function middleware(request: NextRequest) {
return NextResponse.next();
}
修正後:
// セキュリティヘッダーを追加
export function middleware(request: NextRequest) {
const response = NextResponse.next();
response.headers.set(
"Content-Security-Policy",
"default-src 'self'; script-src 'self' 'unsafe-inline'"
);
response.headers.set("X-Content-Type-Options", "nosniff");
response.headers.set("X-Frame-Options", "DENY");
response.headers.set("X-XSS-Protection", "1; mode=block");
response.headers.set(
"Referrer-Policy",
"strict-origin-when-cross-origin"
);
return response;
}
export const config = {
matcher: ["/((?!_next|static).*)"],
};
監査を開発フローに組み込むメリット
定期的にClaude Codeでセキュリティ監査を実施することで、以下のメリットが得られます:
- 早期発見 - 手動レビューでは見落としがちな脆弱性を検出
- 学習効果 - 修正コードからセキュリティベストプラクティスを習得
- 継続的改善 - 開発プロセスにセキュリティチェックを組み込める
Claude Codeのエージェント機能を活用し、より堅牢なアプリケーション開発を実現してください。
この記事が参考になったらLGTMお願いします!
Zennでも技術記事を発信中です → https://zenn.dev/rcn_article