自作の認証よりもNextAuth.js などの実績あるライブラリの方が安全か?
今作っているプロダクトについて、自作の認証を入れましたが、NextAuth.js などの実績あるライブラリの方が有効かどうか、検討した結果をメモします🙏
本プロジェクトの認証の概要
方式: メールアドレス + パスワード による認証で、ログイン後に JWT(アクセス+リフレッシュ) を HttpOnly Cookie に乗せてセッション管理しています。Bearer トークンでの API 認証にも対応しています。
パスワード漏洩リスクに対する対策
1. パスワードの保存(平文は保存していない)
-
bcrypt でハッシュ化した値のみを DB に保存しています(
lib/auth.ts)。 - ラウンド数は 12(
BCRYPT_ROUNDS = 12)で、一般的な強度として問題ない水準です。 - DB の
UserモデルにはpasswordHashのみがあり、平文パスワードは保存していません(prisma/schema.prisma)。
export async function hashPassword(password: string): Promise<string> {
return bcrypt.hash(password, BCRYPT_ROUNDS);
}
export async function verifyPassword(
password: string,
hash: string
): Promise<boolean> {
return bcrypt.compare(password, hash);
}
- 登録時・パスワードリセット時も、いずれも
hashPasswordを通したハッシュだけを保存しています(app/api/auth/register/route.ts,app/api/auth/reset-password/confirm/route.ts)。
2. パスワード強度のチェック
-
8文字以上かつ英字と数字の両方を含むことを必須にしています(
lib/auth.tsのvalidatePasswordStrength)。 - 登録・パスワード変更の両方でこのチェックを実行しています。
3. ログへのパスワード出力
- 認証系 API の
catchではconsole.error('[login]', e)のように 例外オブジェクトeのみをログに出しており、リクエストボディ(パスワード)はログに含めていません。平文パスワードがログに流れる実装にはなっていません。
4. セッション(Cookie)の設定
- 認証用 Cookie は HttpOnly のため、JavaScript から参照できず XSS でトークンを読まれにくい設計です。
- 本番では Secure が有効(
NODE_ENV === 'production')で、HTTPS 前提です。 - SameSite: 'lax' により、基本的な CSRF リスクを抑えています。
5. メール認証
- 登録後、メール認証が完了するまでログイン不可です(
app/api/auth/login/route.tsでemailVerifiedをチェック)。 - 認証用トークンは
randomBytes(32).toString('hex')で生成し、有効期限付きで管理しています。
6. パスワードリセット
- リセット用トークンも
randomBytes(32).toString('hex')で生成し、24時間の有効期限を設けています。 - リセット完了後にトークンと有効期限を null にして無効化しています(二度使い防止)。
7. ログイン失敗時のメッセージ
- 「メールアドレスまたはパスワードが正しくありません」のように、ユーザー存在の有無を区別しない文言で返しており、ユーザー列挙を抑えています。
環境・運用で気にしたい点
-
JWT_SECRET:
lib/auth.tsで「32文字以上必須」とチェックしています。推測困難で長い秘密鍵を本番で設定し、漏洩しないよう管理する必要があります。 -
HTTPS: 本番では Cookie の
secureが true になるため、HTTPS の運用が前提です。 - レート制限: ログイン・パスワードリセットなどに対するレート制限はコード上は見当たりません。ブルートフォースやリセットメール濫用対策として、リバースプロキシやアプリ層での制限を検討するとよいです。
現在の実装まとめ
- パスワードは 平文では保存・ログ出力しておらず、bcrypt ハッシュのみを扱っています。
- セッションは HttpOnly / Secure(本番) / SameSite が設定された JWT Cookie で管理されています。
- メール認証・パスワードリセットはトークン+有効期限で制御されています。
設計としては、パスワード漏洩を抑えるための一般的な対策(ハッシュ化・強度チェック・ログに載せない・Cookie の安全な設定)は取り入れられています。運用では JWT_SECRET の管理 と、必要に応じた レート制限の追加 を検討するとよいです。
結論から言うと、「より安全か」は目的次第です。
- **パスワードの扱い(漏洩リスク)**については、現状の自前実装でも NextAuth.js に乗り換えても大きな差はありません。
- 実装の実績・運用面・OAuth 利用を考えると、NextAuth.js を検討する価値はあります。
以下、整理して説明します。
1. パスワードまわりは「ライブラリで変わるか」
NextAuth.js の Credentials プロバイダー を使う場合でも、
- パスワードのハッシュ化・照合
- DB への保存・取得
は 自分で書くか、別ライブラリに任せる形になります。NextAuth が「パスワードを安全に保存してくれる」わけではありません。
そのため、
- 平文パスワードを保存しない
- bcrypt などでハッシュ化する
- ログにパスワードを出さない
といった点は、今の自前実装でも NextAuth 導入後も、ほぼ同じ責任で実装することになります。
「パスワード漏洩リスク」だけを見ると、「NextAuth にすれば自動で安全」にはなりません。
2. NextAuth.js を使うと「安全になりやすい」部分
NextAuth.js の利点は、主に 認証フロー全体の実績と、自分で書かなくてよい部分 にあります。
| 観点 | 自前実装 | NextAuth.js |
|---|---|---|
| セッション・Cookie の設計 | 自分で設計・検証が必要 | 多くの環境で使われた形が用意されている |
| CSRF 対策 | 自分でトークン等を用意 | 組み込みの CSRF 保護 |
| セッション固定など | 自前で考慮が必要 | ライブラリ側である程度カバー |
| セキュリティ修正 | 自前で追いかけが必要 | メンテナンス・パッチに乗れる |
| OAuth(Google/GitHub 等) | 自前実装は手間・ミスが増えやすい | プロバイダー追加が容易 |
つまり、
- 「パスワードの保存・照合」の安全性 → 今の実装でも NextAuth でも、設計次第でどちらも安全にできる。
- 「認証フロー・セッション・OAuth」の安全性・手間 → NextAuth の方が、実績と手間の面で有利になりやすい。
3. このプロジェクトの現状をどう見るか
いまの実装は、
- bcrypt(ラウンド 12)
- HttpOnly / Secure / SameSite の Cookie
- 平文パスワードを保存・ログに出していない
- メール認証・パスワードリセットのトークン管理
といった点で、一般的なベストプラクティスに沿っています。
「自前だから絶対に危険」というわけではありません。
4. どういうときに NextAuth.js を選ぶとよいか
次のような場合は、NextAuth.js を検討する価値が高いです。
- Google / GitHub などのソーシャルログイン を入れたい(パスワードを減らせる+実装の手間とバグを減らせる)。
- セッション・CSRF・トークン回り を「自分で設計・検証し続ける」よりも、実績あるライブラリに任せたい。
- セキュリティアップデート を自前で追いかけるリソースを減らしたい。
逆に、
- メール+パスワードのみ でよく、現状の実装に自信がある。
- 既存の認証フロー(JWT・Cookie 名・クライアント連携)を大きく変えたくない。
といった場合は、無理に NextAuth に移行しなくても、現状の設計を維持しつつ運用で固める選択もありです。
5. まとめ
- パスワード漏洩などのリスクについては、NextAuth.js を使ったからといって自動で安全になるわけではなく、今の bcrypt+平文保存なしの実装でも、同等レベルまで安全にできます。
- 「実績あるライブラリで全体を固めたい」「OAuth を入れたい」「セッション・CSRF をライブラリに任せたい」 という意味では、NextAuth.js を使う方が安全・安心しやすいと言えます。
なので、
- 「パスワード漏洩が心配」 → 現状の実装を維持しつつ、JWT_SECRET や HTTPS などの運用をしっかりするのでもリスクは抑えられます。
- 「認証全体を実績ある形にしたい/OAuth を入れたい」 → NextAuth.js の導入を検討する、という整理がしっくりきます。
Discussion