個人開発:Next.js はじめてのセキュリティ対策覚書
初めての個人開発です。認証系のアプリケーション開発時における現時点での BaaS やウェブアプリケーション(Next.js など)のセキュリティ対策の覚書。チェックリストなので深くは掘り下げない。更新、追加、修正予定あり。
データセキュリティ
RLS の有効化とポリシー設定
例えば、Supabase を使う場合、RLS を有効にしてポリシーを適切に作成。各ユーザーがアクセスできるデータを厳密に制御する。
RLS(Row-Level Security)はデータベースのテーブルでユーザーごとに異なるアクセス制限を設定する仕組み。例えば、自分の投稿だけが見えるようにするなど、柔軟な制御が可能。
ポリシーとは、各ユーザーがどのデータにアクセスできるかを定めた具体的なルールのこと。これにより、データ漏洩のリスクを低減し、ユーザーごとの権限を厳格に管理できる。
Supabase は、管理画面から RLS の設定ができる。また、テーブルの Database role settings から、セキュリティの設定が適切にできているかを確認できる。
ビューを利用して機密情報を非表示
ビューは、既存のテーブルの任意のカラムを選択して表示することができる。複数のテーブルの任意のカラムを結合して表示することもできるので便利。
機密情報が書いてあるカラムは常に非表示にして、ビューを利用し、カラムごとに選択を制御する。全ユーザーに読み取り権限を許可しつつ、ビューを通して制限付きカラムのみ隠す。データへのアクセスはビュー経由で行う。
- 制限付きカラムを除外し、残りのカラムだけを表示するビューを作成
- ビューに対して SELECT 権限を設定し、RLS を適用
- ビューのアクセス権設定
- ビューのデータ利用
機密情報が直接露出しないため、誤操作や不正アクセスのリスクが減少する。
データ取得時の limit 付与
大量のデータを不正に取得されないようにするための対策。
API でデータを取得する際に、デフォルトで取得件数を制限。不必要に大量のデータが漏れるリスクを低減。
個人開発における単体テスト計画
時間的なコストを避けないフルスタックな個人開発なので、どのファイルから自動テストを始めるか優先順位を決めて進めた。
- ビジネスロジック(/lib/~)
- API レイヤー(/src/app/api/~)
- ユーティリティ関数(/utils/~)
- データ取得ロジック(page.tsx)
- コンポーネント(/components/~ 。時間がないなら省略)
カバレッジ 100%よりも、バグが致命的になるビジネスロジック(認証、認可、データの加工・検証、業務フロー)の自動テストに集中。E2E テスト(Playwright など)の割合を減らして、単体テストに重点を置いた。
ページがデータを取得して表示する場合、そのデータ取得ロジックやエラーハンドリングをテストするコードを書く。時間節約のため、チケット(開発背景や目的)や単体テスト項目は直接コードのドキュメントに書く。
例えば、必要なデータが正しく取得されること、エッジケースにも対応していることを確認する場合、以下のように設計。
- 正しいクエリでデータが取得できるか
- 空の結果(0 件のデータ)でもエラーにならないか
- エラーが発生した場合に適切に処理されるか
認証・認可を組み込んで、外部から悪用されないようにする
認証を組み込み、不正アクセスを防止する。サーバーで非ログイン者を判定し、認証されたユーザーのみにデータを返す。クライアントでは認証しない。
サーバーでもサニタイズ処理
クライアントコンポーネントだけでなく、サーバーコンポーネントでも DOMPurify などでサニタイズ処理を行う。フォーム、コメント、検索、タグ名、記事一覧、マイページ、管理画面、ページネーションなど全て。
クライアントのサニタイズを無効にしてテストを行う。
use server
use server
は、インライン関数の先頭に記述する。データベース操作している関数が露出することがなくなる。
データ取得( select, insert などを記載しているファイル )関数は、/lib/
以下にまとめ分離する。
データを実際に呼び出す箇所で "use server"
を付ける。
import 'server-only'
機密情報を取得しているファイルには、import 'server-only'
をつける。
クライアントで直接データを取得しない
クライアントコンポーネントで、api や データ関数など、直接データを取得しない。サーバー側で処理を行い、その結果をクライアントに渡す。
Next.14 の layout で認証しない
v14 では、Layout より先に Page がレンダリングされるので、Layout で認証を行っても Page の内容がソースに表示されてしまう可能性がある。v15 では改善された。
layout.tsx
にはデータ取得のロジックも記載しない。メタに動的な処理が必要であれば page.tsx
で行う。
引数の順番に気をつける
サーバーコンポーネントから、クライアントコンポーネントに値を渡すとき、引数の順番と型の一貫性に気をつける。
TypeScript の型チェックにより間違いに早く気づけるが、順番に意識が向いていないと型チェックをすり抜けるケースも考えられる(any や unknown 型を使用した場合)。
any を削除し修正。型チェックのみ実行
any を削除して書き換え後、npx tsc --noEmit
でエラーが出ないか確認
ウォッチャモード
-w
オプションを追加すると、ファイルの変更を監視して自動的に型チェックを実行。
SELECT *
で全て取得しない
クエリ結果からどのデータを渡すべきかが曖昧になるから、DTO(Data Transfer Object)での制御が難しくなる。
そもそも、全てのカラムを取得すると、ネットワーク負荷やメモリ消費が増大し、アプリケーションのパフォーマンスに悪影響を及ぼすし、テーブルに新しいカラムが追加されたら、既存のクエリが予期せぬデータを取得する可能性がある。
サーバーサイドで DTO を使用し、取得したデータをクライアントに渡す際に必要な形式に整形する。
アクセストークンはクッキーに保存
認証の安全性と利便性を確保するために、アクセストークンはローカルストレージではなくクッキーに保存する。
クッキーに保存する際に httpOnly 属性を設定することで、クライアントサイドの JavaScript からのアクセスを防ぎ、XSS 攻撃から保護できる。
自動バックアップ設定
データ消失を防ぐため、日次で自動バックアップ(ポイントインタイムリカバリ対応)を実施。
ポイントインタイムリカバリ(PITR)は特定の時間にデータを復元するための仕組み。誤操作やシステム障害が発生しても迅速に復旧が可能。
ストレージのアクセス制御
アップロード画像へのINSERT
操作のみ許可することで、データの誤操作や不正な操作を防止。
アクセス制御を行うことで、誤ったファイルの削除や変更からデータを保護。セキュリティが強化され、データの一貫性が確保される。
データ暗号化
すべてのユーザーデータをデータベース内で暗号化し、不正アクセスから保護。
暗号化により、データが漏洩した場合でも内容を第三者が読み取ることは困難。プライバシーとセキュリティを強化。
バックアップの実施
データの消失を防ぐために、日次および週次でバックアップを行う。
バックアップはクラウド上で安全に保管され、物理的な損失リスクも軽減。障害や攻撃に対しても迅速な復元が可能。
DTO の導入
DTO(Data Transfer Object) の最大のセキュリティ的なメリットは、クライアントに渡すデータを制御すること。データベースクエリで取得する情報には、機密性の高いデータやクライアントに不要なデータが含まれていることがある。DTO を使うことで、取得したデータの中から必要な部分だけを選んでクライアントに渡すことができる。
サーバーサイドとクライアント間でやり取りするデータの内容を厳密に定義し、どのデータをクライアントに送ってよいかを明確に管理できる。これにより、不要なデータが漏れることを防ぐ。
Taint API
Taint API は、React(特に Next.js 14 での実験的機能として)が提供するセキュリティ機能。この API を使うことで、サーバー側でのみ使用すべきオブジェクトやデータに「クライアント側では使えない」というマークをつけることができる。
Taint API を利用すると、特定のオブジェクトや値に「クライアントで使うことは許可されていない」というラベルを付けられる。このラベルが付いたデータをクライアント側に渡そうとすると、実行時にエラーが発生する。これにより、クライアントで使用してはいけないデータが誤って公開されることを防ぐことができる。
認証とアクセス制御
SOC 2、HIPAA 準拠
高いセキュリティおよびプライバシー基準を満たしていることを証明できる BaaS を選択。
SOC 2 はデータの保護に関する基準、HIPAA は医療情報の保護に関する基準。顧客のデータ保護を確実に行うことができる。
ロールベースのアクセス制御(RBAC)
各ユーザーの役割に応じて適切なアクセス権を付与。
RBAC は、ユーザーの職務や責任に応じて必要な権限のみを与える仕組み。最小権限の原則を遵守し、セキュリティリスクを減少させる。
ブルートフォース攻撃対策
多重ログイン試行に対する保護策を講じる。
ブルートフォース攻撃は多数のパスワードを試行してログインを試みる攻撃。これを防ぐために、ログイン試行回数の制限や CAPTCHA を導入。
GitHub の 2FA 有効化
バージョン管理システムのセキュリティ強化と、連携している BaaS への不正アクセス対策。
2FA(2 要素認証) を有効にすることで、パスワードに加えて追加の認証手段が必要。不正アクセスのリスクを大幅に減少。
Cookie に HttpOnly 属性を付与
クライアントサイド JavaScript によるアクセスを制限し、セッション情報の漏洩を防ぐ。
HttpOnly 属性を付けることで、JavaScript を使った不正なセッションハイジャックを防止。クッキーに保存されたセッション情報がより安全に保護される。
MFA の導入
ユーザーのアカウント保護を強化するために、多要素認証(MFA)を導入。
MFA(Multi-Factor Authentication) はパスワード以外にも追加の認証要素を要求。アカウント乗っ取りのリスクが低減される。
サニタイズ処理
サーバーサイドのバリデーション
クライアント側での不正なデータ送信を防ぐため、サーバーサイドでも入力の検証を行う。
クライアント側だけでなくサーバー側でもバリデーションを行うことで、攻撃者による改ざんを防止。データの一貫性と信頼性が保たれる。
DOMPurify
DOMPurify、URL スキームの制限。全てのフォームに対して、危険なスクリプトや URL の挿入を防ぐ。
DOMPurify などのライブラリを使って、HTML やスクリプトの不正な入力を除去。XSS 攻撃などのリスクを軽減。
フォームごとのサニタイズと制限
検索、コメント、プロフィールなどにおいて、文字数、リンク数、画像数の制限を設け、不適切な使用を防ぐ。
スパムや攻撃に利用されるリスクを低減。制限設定を明確にすることで、利用者にとっても予測可能な入力制御を提供。
XSS 対策
CSP の設定
middleware
で適切な CSP(Content Security Policy) ポリシーを設定し、XSS 攻撃を防ぐ。
CSP により、許可されたソースからのコンテンツのみ読み込むように制限。不正なスクリプトの実行を防止。
nonce の導入
nonce(ナンス)は、CSP(Content Security Policy)の一部としてスクリプトの実行を安全にするための一時的なトークン。
nonce を使用することで、特定のスクリプトだけを実行可能にし、XSS(Cross-Site Scripting)攻撃を防止する効果がある。nonce はページ読み込みごとにランダム生成され、そのページでのみ有効なため、外部からの悪意あるスクリプトの挿入を防ぐ。
Next.js での CSP 実装の場合、動的に nonce
を出力する必要があるので src/middleware.ts
に書く。
next.config.mjs
に書くと、静的出力になるので nonce
を生成できない。
src/middleware.ts
に書かれた CSP は、サーバーコンポーネントでのみ有効になる。
できるだけ、unsafe-eval
や unsafe-inline
を使わないようにする。
unsafe-inline の排除
インラインスクリプトを防止し、CSP の強化。
unsafe-inline
は XSS 攻撃のリスクを高めるため、これを排除することでセキュリティを向上。より堅牢な CSP ポリシーが実現される。
noopener noreferrer
target="_blank"
リンクにnoopener noreferrer
を付与。新しいタブを開いた際に元ページが影響を受けないようにする。
noopener noreferrer
を付与することで、リンク先のページが元のページを操作するリスクを防ぐ。クリックジャッキングやタブナビゲーションの脆弱性が軽減される。
クリックジャッキング対策
クリックジャッキングは、ウェブページを透明なレイヤーで覆い、ユーザーが意図しないボタンをクリックさせる攻撃。
X-Frame-Options
ヘッダーを使用して、この攻撃からウェブアプリケーションを保護。信頼されていないサイトによるページの埋め込みを防止。
クロスオリジン対策
ネットワークと通信のセキュリティ
SSL の有効化
通信を暗号化し、第三者による盗聴を防止。
SSL/TLS により、通信データを暗号化することで中間者攻撃を防ぐ。データの機密性と完全性が確保される。
CORS の適切な設定
API のレスポンスに CORS(Cross-Origin Resource Sharing) ヘッダーを追加し、安全な通信を保証。
CORS は異なるオリジン間でのリソース共有を安全に行うための仕組み。悪意あるドメインからの不正なリクエストを防止。
CORS と next.config.mjs
next.config.mjs にAccess-Control-Allow-Origin
などを設定
外部リソースへの適切なアクセス制御を行うため、CORS 設定を next.config.mjs に追加。信頼されたドメインからのアクセスのみを許可。正しい CORS 設定により、不正なクロスオリジンリクエストを防止。
コンテンツ制御
マークダウンに対する XSS 対策
マークダウンが悪用されないよう、適切なサニタイズ処理を実施。
Markdown は HTML としてレンダリングされることがあるため、XSS のリスクがある。適切なサニタイズにより、不正なスクリプト実行を防ぐ。
禁止ワードとアフィリエイトリンク対策
投稿内容に適切なフィルターを適用し、ルールに反するコンテンツをブロック。
禁止ワードリストやリンクフィルターを設けることで、スパム投稿を防止。サービス全体の品質とユーザーエクスペリエンスが向上する。
404 ページと検索ページの noindex
不要なページが検索エンジンにインデックスされないように制御。
noindex
メタタグを設定することで、検索結果から除外。無関係なページが検索エンジンに表示されることを防止。
メールと通信管理
DKIM、DMARC、SPF の設定
メールの信頼性を高め、フィッシングメールを防ぐ。Email を API 経由で送信するサービスでは、DKIM、DMARC、SPF の設定ができるサービスを選ぶこと。
Resend では、管理画面から設定できる。
これらの設定により、なりすましメールを検出し、正当なメールのみが配信されるようにする。メールの受信者に対する信頼性が向上。
重複チェックの事前実施
申請フォームでの重複送信を防止。
同一のリクエストが何度も送信されることを防ぐことで、サーバーへの負担を軽減。サーバーリソースの効率的な利用が可能。
CSRF 対策
セッションとユーザー体験
CSRF 対策
トークンを使用して、セッションハイジャックを防止。
CSRF(Cross-Site Request Forgery)トークンを使用することで、正規のリクエストのみが処理されるようにする。ユーザーのセッションを狙った攻撃を防止。
法的なリスクを減らす
ログイン時にプライバシーポリシーへの同意
ログイン時に利用規約とプライバシーポリシーへの同意を確認。ユーザーの同意を取得し、トラブルを防止。
利用規約とプライバシーポリシーへの同意を明示的に確認。法的なリスクを減らす。
非ログイン時のユーザー体験改善
ブックマークボタンを押した際にポップアップでログインを促し、UX を向上。
未ログインユーザーに対して適切なプロンプトを表示。利用者の利便性と体験を向上させる。
管理ツールとその他の対策
next.config.mjs
以下のヘッダー設定により、ウェブアプリ全体のセキュリティを強化。
X-Content-Type-Options
Strict-Transport-Security
X-Frame-Options
Referrer-Policy
ウェブアプリが攻撃者からの悪意ある操作に対してより強固になる。
X-Content-Type-Options
X-Content-Type-Options: nosniff
ヘッダーを設定することで、ブラウザがコンテンツの MIME タイプを信頼できる方法で処理するよう強制し。これにより、MIME タイプスニッフィングによる攻撃を防ぐ。
Strict-Transport-Security
Strict-Transport-Security
ヘッダーを利用し、ブラウザに対して HTTPS のみを使用するよう指示。これにより、中間者攻撃(MITM)や HTTP ダウングレード攻撃を防止する。
X-Frame-Options
X-Frame-Options
ヘッダーを利用して、サイトが他のページに埋め込まれることを防ぐ。クリックジャッキング攻撃のリスクを低減する。
Referrer-Policy
Referrer-Policy
ヘッダーを設定し、リファラー情報の送信範囲を制御。これにより、機密情報の漏洩を防ぐ。
depcheck で不要なパッケージ削除
メンテナンス性の向上とセキュリティリスクの低減。
不要なパッケージは脆弱性の原因となるため、depcheck(CLI コマンド)を使用して定期的に確認し削除。コードベースがクリーンになり、セキュリティが向上。
admin ページ
admin ページの API 化
管理ページのフェッチ部分を API 化することで、バックエンドとの連携を強化。
API を使用することで、フロントエンドとバックエンドが独立し、再利用性が向上。保守性と拡張性が向上。
多要素認証(MFA)
admin ページにアクセスする際には、パスワードに加えて追加の認証手段を導入し、不正アクセスのリスクを低減する。これにより、万が一パスワードが漏洩しても、攻撃者がアクセスするのを防ぐことができる。
IP ホワイトリスト
特定の IP アドレスからのみ admin ページにアクセス可能とすることで、未知の場所からの不正なアクセスを制限する。これにより、信頼できるネットワークからのみアクセスできるようにする。
レートリミッティング
ログイン試行の回数を制限し、ブルートフォース攻撃を防止する。一定回数のログイン失敗後にアクセスを制限することで、攻撃のリスクを低減する。
VPN を使用したアクセス制限
admin ページには社内ネットワークや VPN 経由でのみアクセス可能にすることで、外部からの攻撃のリスクを大幅に減少させる。この対策は内部ユーザーに対して限定的なアクセスを許可することに役立つ。
ゼロトラストセキュリティ
ゼロトラストネットワークの導入
ゼロトラストセキュリティモデルでは、すべてのアクセスを「信頼しない」ことが原則。
ユーザーやデバイスの認証と認可を常に行い、最小限の権限を付与することで、内部と外部の区別なく全てのアクセスを厳格に管理。ネットワークのセグメンテーションやアクセスの微細なコントロールがセキュリティを強化。
継続的な認証と検証
ユーザーの行動パターンやデバイスを継続的に監視し、リスクが高いと判断された場合には再認証を要求。
不正アクセスの兆候を早期に発見し、迅速な対策を講じることが可能。
脅威検知と自動対応
脅威検知の AI/ML 技術の利用
AI および機械学習を活用して、異常な振る舞いや脅威を自動的に検出。
通常のシグネチャベースの検知では見逃されるような新たな攻撃パターンを識別し、迅速に対処することが可能。セキュリティイベントのリアルタイム監視により、潜在的な脅威を早期に検知。
自動的な攻撃対応システムの導入
検出された脅威に対して自動的に対応するシステムを導入。
例えば、特定の異常な動作が検知された場合、自動で該当するデバイスやユーザーアカウントを隔離し、さらなる被害を防ぐ。これにより、人手による対応の遅延を防止し、即座にリスクを軽減することが可能。
セキュリティ対策の導入
WAF の導入
WAF(Web Application Firewall)は、Web アプリケーションへの不正なリクエストを検出・ブロックするための防御ツール。
WAF の導入により、開発者はアプリケーションのコードに変更を加えることなく、セキュリティレベルを大幅に向上させることが可能。特に、既知の脆弱性に対する迅速な対策が求められる場合に非常に有効。
WAF レート制限
レート制限でブルートフォース攻撃を防止する。
レート制限を使用すると、特定の時間枠内で同じソースからのリクエストがアプリケーションにヒットする回数を制御できる。
Vercel では、ダッシュボードからレート制限を設定できる。
Threat Intelligence
Threat Intelligence(脅威インテリジェンス)は、サイバー脅威に関する情報を収集・分析し、攻撃の兆候を早期に検知するための手法。
Threat Intelligence の活用により、攻撃者の行動パターンや最新の脅威情報を把握し、プロアクティブなセキュリティ対策が可能になる。これにより、潜在的な攻撃に対して迅速な対応が行えるため、セキュリティインシデントのリスクを大幅に低減することができる。
プライバシー法への準拠
GDPR(General Data Protection Regulation)、CCPA(California Consumer Privacy Act)などのプライバシー法に準拠することは、ユーザーデータの適切な管理や保護のために重要。
これらのプライバシー法は、データ収集、保管、共有に関して厳格なガイドラインを提供している。準拠することで、ユーザーのプライバシーを保護し、法的リスクを軽減することが可能。また、ユーザーからのデータ削除リクエストや情報開示要求に対応するためのプロセスを整えることが求められる。
バグ報奨金プログラム
バグバウンティプログラムを導入する。第三者からのセキュリティ改善提案を受け付ける仕組みを作ると、セキュリティの向上に役立つ。
ユーザーに対して、セキュリティリスクや安全な操作方法についての教育を行う仕組みを導入することで、人的要因によるリスクを減少させることができる。
終わりに
UI の単純化(色々やらない)
フロントエンドの UI をシンプルにし、不要な機能をつけない。色々やらない。ユーザーがほとんど利用しない機能やオプションは削除。
初期段階では基本機能のみを公開し、ユーザーのフィードバックを基に必要な機能を後から追加。単体テストは最初に作ったほうがいいかも。
不要なフォームや設定項目は、潜在的な脆弱性の温床になる。
継続的なセキュリティ対策ができたらいいなと思っています。