Hacktoberfest 2026:メンテナが10月に向けて印を付けた、オープンで初心者向けの issue。 Hacktoberfest の issue を見る

Email OTP login has no attempt limit: useVerificationToken doesn't invalidate the code on a wrong guess (brute-forceable account takeover)

オープン
#2,221 コメント 2 件 リアクション 0 件 担当者 0 名 GitHub で見る

まだ誰も着手していません。

評価

難易度
3/5
見積もり時間
1〜2日
初心者へのやさしさ
78/100
issue の種類
バグ
明瞭さ
明確に書かれている
活発さ
活発
技術スタック
nextjs, typescript

調査の方向性

packages/database/auth/drizzle-adapter.ts の useVerificationToken から始め、apps/web/app/api/mobile/[...route]/route.ts:543-548 と不一致時の処理を比較してください。動作を検証する前に、メール callback のエントリーポイントと既存の auth チェックを確認してください。誤ったコードによって検証行が無効化され、再試行できなくなり、正しい検証は引き続き機能する状態になれば完了です。

索引モデルが issue の本文から書いたものです。

説明

bug
Summary

useVerificationToken in packages/database/auth/drizzle-adapter.ts (lines 512-538) only
deletes the verification row on a successful match. A wrong 6-digit code guess finds no row
(lookup is by exact token value), logs a warning, and returns null the real code is left
untouched. It can be guessed without limit for its entire TTL (10 minutes as of #2068).

This is a logic bug, not just a missing rate limit: the mobile login path already does this
correctly. apps/web/app/api/mobile/[...route]/route.ts:543-548 deletes the row on a token
mismatch before returning "invalid", burning the code on the first wrong attempt. The web
(NextAuth) path has no equivalent.

Repro
  1. POST /api/auth/signin/email for a target address.
  2. Fire guesses at GET /api/auth/callback/email?email=<target>&token=<6-digit>.
  3. Codes come from crypto.randomInt(100000, 1000000) (900,000 possible values,
    auth-options.ts:138). At ~100 req/s, ~6.7% success chance within the 10-minute window;
    sustained guessing lands a hit in a few hours.
Why this isn't closed by existing rate-limit ids

RATE_LIMIT_IDS.AUTH_OTP_VERIFY / AUTH_OTP_SEND (apps/web/lib/rate-limit.ts:83-85) are
declared but have zero call sites (see #2039, PR #1924 open and stale since June, PR #2040
closed without wiring these two). Even once wired, isRateLimited is backed by Vercel Firewall
and fails open without a matching dashboard rule, so it provides no protection on self-hosted
deployments
regardless. The durable, hosting-agnostic fix has to live in
useVerificationToken itself.

Suggested fix

Delete (or otherwise invalidate) the verification row on a failed match too, not only on
success mirroring the mobile implementation. Wiring AUTH_OTP_VERIFY/AUTH_OTP_SEND as
defense-in-depth on Vercel deployments is worth doing separately, but shouldn't be treated as
the fix on its own since it doesn't cover self-hosted instances.

Related
  • #2039 (open), broader unwired-rate-limit-ids issue, includes these two ids but not this root cause
  • #1924 (open, stale), Firewall-based rate limiting, never merged
  • #2068 (merged), fixed the 24h→10min window, explicitly left the attempt-counter fix as follow-up
  • #2040 (closed), deliberately punted on wiring these two ids
主要言語
Rust
スター
22.5k
フォーク
1.9k
平均マージ
7時間 43分
マージ済み PR(30日)
80

コントリビューションガイド

コントリビューションガイドを開く

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

CapSoftware/Cap のほかの issue

CapSoftware/Cap の issue をすべて見る

似ている issue

Rust の issue をもっと見る

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。