Menu

ログインレート制限設定ガイド

このドキュメントでは、NEXTY.DEV ボイラープレートにおける認証エンドポイントの IP ベースのレート制限を設定する方法を説明します。レート制限は、メールスパム、ブルートフォース攻撃、無料枠リソースの過剰使用などの悪用を防ぐのに役立ちます。

概要

NEXTY.DEV はデフォルトで Better Auth の組み込みレート制限機能 を使用し、サーバーレス環境向けに Upstash Redis のサポートもオプションで提供しています。

デフォルト設定

lib/auth/index.ts
rateLimit: {
  enabled: true,
  window: 60, // 60秒
  max: 100,   // ウィンドウあたり100リクエスト(グローバルデフォルト)
  customRules: {
    "/sign-in/magic-link": { window: 60, max: 3 },
    "/email-otp/send-verification-otp": { window: 60, max: 3 },
    "/sign-in/email-otp": { window: 60, max: 5 },
  },
}
エンドポイントウィンドウ最大リクエスト数説明
/sign-in/magic-link60秒3マジックリンクログインメールリクエスト
/email-otp/send-verification-otp60秒3OTP 認証コードメールリクエスト
/sign-in/email-otp60秒5OTP 認証試行
その他すべてのエンドポイント60秒100グローバルデフォルト

カスタマイズ例

シナリオ1: 無料枠の悪用を防ぐための厳格な制限

ユーザーがアカウントを切り替えて無料枠のクレジットを悪用することが懸念される場合、より厳格な1日あたりの制限を追加します:

lib/auth/index.ts
rateLimit: {
  enabled: true,
  window: 60,
  max: 100,
  customRules: {
    // 厳格なメール送信制限
    "/sign-in/magic-link": { 
      window: 60 * 60 * 24, // 24時間
      max: 5, // IP あたり1日5件のマジックリンクのみ
    },
    "/email-otp/send-verification-otp": { 
      window: 60 * 60 * 24, // 24時間
      max: 10, // IP あたり1日10件の OTP コードのみ
    },
    // 認証試行を妥当な範囲に保つ
    "/sign-in/email-otp": { 
      window: 60 * 60 * 24, // 24時間
      max: 20, // 24時間あたり20回の試行
    }
  },
  ...(redis && {
    customStorage: {
      get: async (key: string) => {
        const data = await redis!.get<{ key: string; count: number; lastRequest: number }>(key);
        return data || undefined;
      },
      set: async (key: string, value: { key: string; count: number; lastRequest: number }) => {
        await redis!.set(key, value, { ex: 60 * 60 * 24 }); // 24時間後に自動期限切れ
      },
    },
  }),
}

シナリオ2: 非常に厳格な悪用対策(高価値サービス)

無料枠の悪用がコストのかかるサービス(例: AI クレジット、API コール)の場合:

rateLimit: {
  enabled: true,
  window: 60,
  max: 50, // グローバルデフォルトを低く設定
  customRules: {
    // 極めて厳格なメール制限
    "/sign-in/magic-link": { 
      window: 60 * 60 * 24, // 24時間
      max: 3, // IP あたり1日3件のマジックリンクのみ
    },
    "/email-otp/send-verification-otp": { 
      window: 60 * 60 * 24, // 24時間
      max: 5, // IP あたり1日5件の OTP コードのみ
    },
    // 厳格な認証制限
    "/sign-in/email-otp": { 
      window: 60 * 60 * 24, // 24時間
      max: 20, // 24時間あたり20回の試行
    },
  },
  ...(redis && {
    customStorage: {
      get: async (key: string) => {
        const data = await redis!.get<{ key: string; count: number; lastRequest: number }>(key);
        return data || undefined;
      },
      set: async (key: string, value: { key: string; count: number; lastRequest: number }) => {
        await redis!.set(key, value, { ex: 60 * 60 * 24 }); // 24時間後に自動期限切れ
      },
    },
  }),
}

シナリオ3: 緩和された制限(内部ツール / 低リスク)

内部ツールや低リスクのアプリケーションの場合:

rateLimit: {
  enabled: true,
  window: 60,
  max: 200, // グローバルデフォルトを増やす
  customRules: {
    "/sign-in/magic-link": { window: 60, max: 10 },
    "/email-otp/send-verification-otp": { window: 60, max: 10 },
    "/sign-in/email-otp": { window: 60, max: 20 },
  },
}

シナリオ4: レート制限を無効化(開発環境のみ)

rateLimit: {
  enabled: false, // ⚠️ 本番環境では絶対にしないでください!
}

ストレージオプション

インメモリストレージ(デフォルト)

Upstash Redis の環境変数を設定していない場合、ボイラープレートに組み込まれた Redis メソッドは有効化されず、レート制限データはメモリに保存されます。

メリット:

  • 外部依存なし
  • シンプルなセットアップ

デメリット:

  • サーバー再起動時にデータが失われる
  • 複数インスタンス間で共有できない。Vercel のようなサーバーレス環境にデプロイした場合、各インスタンスが独自のメモリを持つため、レート制限が効果的に機能しない

Upstash Redis ストレージ(本番環境推奨)

UPSTASH_REDIS_REST_URLUPSTASH_REDIS_REST_TOKEN が設定されている場合、ボイラープレートは自動的に Redis を使用します。

lib/auth/index.ts
...(redis && {
  customStorage: {
    get: async (key: string) => {
      const data = await redis!.get<{ key: string; count: number; lastRequest: number }>(key);
      return data || undefined;
    },
    set: async (key: string, value: { key: string; count: number; lastRequest: number }) => {
      await redis!.set(key, value, { ex: 120 }); // 120秒後に自動期限切れ
    },
  },
}),

メリット:

  • 再起動後もデータが保持される
  • すべてのインスタンス間で共有される
  • サーバーレス環境(Vercel、Cloudflare Workers)に最適
  • TTL による自動データクリーンアップ

デメリット:

  • Upstash アカウントが必要(無料枠あり)

Upstash Redis のセットアップ

Upstash 連携 の手順を参照してください。

データベースストレージ

注目すべき点

  • このアプローチはボイラープレートに組み込まれていません。このソリューションを使用したい場合は、ドキュメントに従って自分で実装してください。
  • データベースストレージは期限切れレコードを自動的にクリーンアップしません。古いレコードをクリーンアップするためのスケジュールタスクを設定する必要があります。

Better Auth はデータベースストレージもサポートしています:

lib/auth/index.ts
rateLimit: {
  enabled: true,
  storage: "database",
  modelName: "rateLimit", // テーブル名
  // ...
}

その後、マイグレーションを実行します:

pnpm dlx @better-auth/cli migrate