Workers KV をフォームメールの送信レート制限に使ったメモ

現在 Resend.com で送信するフォームメールを作成していて、セキュリティ機能を実装中。

最終的に実装したい機能は5つで、

  • サニタイゼーション – XSS対策(実装済み)
  • 入力値検証 – 不正データ防止(実装済み)
  • CORS設定 – クロスオリジン攻撃防止(実装済み)
  • レート制限 – 大量送信防止
  • Turnstile(CAPTCHA)- ボット対策

上の3つは実装済みなので残りのうち1つの、同じ IP アドレスからの大量送信を防ぐための「送信レート制限」を Workers KV を使って実装をする。

Cloudflare Workers KV とは

Workers のコードから簡単にキー(key)と値(value)のペアを保存しアクセスできるサービス。

Cloudflare のダッシュボードか wrangler コマンドでネームスペースを作成して使う。

npx wrangler kv namespace create RATE_LIMIT_KV

env.RATE_LIMIT_KV で get や put のような操作ができる。

送信レート制限実装

機能の内容は1時間に送信できる上限回数を5回とし、それ以降はエラーを返す。

仕組みとしては、送信される時に KV からその IP アドレスが何回目の送信かを判定して、5回以下の場合だけ送信し、クライアントの IP アドレスをキーにして、カウントした送信回数をそのキーの値として Workers KV に保存する。
6回目以降はステータスコード 429 のレスポンスを返す。
429 はクライアントが指定時間内にたくさんリクエストを送信しすぎたことを示す。

実装箇所は Resend.com へ送信する api ファイルに記述した。
KV のネームスペースは RATE_LIMIT_KV。

...

  // レート制限チェック
  const ip = request.headers.get('CF-Connecting-IP') || 'unknown';
  const rateLimitKey = `rate-limit:${ip}`;

  try {
    // 現在のリクエスト数を取得
    const currentCount = await env.RATE_LIMIT_KV.get(rateLimitKey);
    const count = currentCount ? parseInt(currentCount, 10) : 0;

    // 制限チェック(例: 1時間に5回まで)
    if (count >= 5) {
      console.warn(`Rate limit exceeded for IP: ${ip}`);
      return new Response(
        JSON.stringify({
          error: '送信回数が上限に達しました。送信回数が上限に達しました。しばらく待ってから再度お試しください。',
        }),
        {
          status: 429,
          headers: corsHeaders,
        }
      );
    }

    // カウントを増やす(1時間後に自動削除)
    await env.RATE_LIMIT_KV.put(rateLimitKey, (count + 1).toString(), { expirationTtl: 3600 });
  } catch (error) {
    console.error('Rate limit check error:', error);
    // レート制限のチェックに失敗しても処理を続行
  }

...

参考URL

ページの先頭へ