Next.jsでHttpOnly CookieにJWTを安全に保存する

問題

JWTをlocalStorageに保存すると、XSS攻撃にそのまま晒されます。悪意のあるスクリプトがlocalStorage.getItem('token')でトークンを盗むことができます。

解決方法

// app/api/auth/login/route.ts
import { NextRequest, NextResponse } from 'next/server';

export async function POST(request: NextRequest) {
  const { username, password } = await request.json();
  const user = await authenticateUser(username, password);

  if (!user) {
    return NextResponse.json(
      { error: 'Invalid credentials' },
      { status: 401 }
    );
  }

  const token = generateToken(user.id, user.username);
  const response = NextResponse.json({ success: true, user });

  // HttpOnly CookieにJWTを保存
  response.cookies.set('auth-token', token, {
    httpOnly: true,                              // JSからアクセス不可
    secure: process.env.NODE_ENV === 'production', // HTTPSのみ
    sameSite: 'lax',                             // CSRF防御
    maxAge: 60 * 60 * 24,                        // 24時間
    path: '/',
  });

  return response;
}

ポイント

  • httpOnly: trueが最も重要です。これを設定するとdocument.cookieでアクセスできなくなるため、XSS攻撃によるトークン窃取が不可能になります。
  • secure: trueはプロダクション環境でのみ有効にすべきです。ローカル開発はHTTPのため、Cookieが送信されない問題が発生します。
  • sameSite: 'lax'は他のサイトからのPOSTリクエスト時にCookieを送信しません。CSRF攻撃の基本的な防御線です。