Next.js・Reactで行うべきセキュリティの対策
フロントエンド開発を行うに当たり最低限行うべき対策。
クロスサイトスクリプティング(XSS)
用語説明
-
XSS: リクエストに含まれる文字列をそのままHTMLへ挿入すると、意図せぬJavaScriptが実行される恐れがある。
-
反射型XSS: 罠サイト経由でアクセスした際、URLのパラメータ等に悪意あるコードを埋め込まれ、SSRでレンダリングされたHTMLから実行される。
-
蓄積型XSS: 攻撃者が投稿したフォームを他のユーザーが閲覧時に実行される。
-
DOM-based XSS: DOMツリーを書き換える系の操作で悪意あるスクリプトを挿入。クライアントサイドで起こるため、検知が難しい。対策として、DOM操作用の関数を利用する。
li.textContent = name;
-
ソース(原因)
- location.hash
- location.search
- location.href
- document.referrer
- postMessage
- Webストレージ
- IndexedDB
-
シンク(実行箇所)
- innerHTML
- eval
- location.href
- document.write
- jQuery()
-
-
CSP: 許可されていないJavaScriptの実行やリソースの読み込みをブロック。metaタグに埋め込み or レスポンスヘッダーに付与することで適用される。
- base-uri: baseURLを変更されないようにする。
- object-src: Flash等のプラグインに対する制限を付与。
- trusted types: 文字列を安全な型として扱う。DOMPurifyで検査可能。
- Strict CSP: CSPを適用させると、HTML内にインラインJavaScriptは禁止される。回避方法として、
unsafe-inline
を使うのではなく、nonce-source
やhash-source
を利用すると安全にインライン記述ができる。nonce-source
: script要素に指定したランダムなトークンがCSPヘッダーに指定されたものと同一でないとエラーを吐くもの。script-source
: script要素のhash値を計算し、それをCSPヘッダーに指定して、同一でないとエラーを吐くもの。strict-dynamic
: 上記を指定しても許可されないscript要素の作成が必要な場合に利用。※innerHTML
やdocument.write
は機能しない。
対策方法
-
dangerouslySetInnerHTML
の利用を禁止する。どうしても利用する必要がある場合は、DOMPurify等を用いてサニタイジングして、html-react-parser等でパースすることでdangerouslySetInnerHTML
の利用を回避する。 -
href
にURLを挿入する場合は、http(s)
以外受け付けないようサニタイジングを行う。const urlObj = new URL(url); if (urlObj.protocol === "http:" || urlObj.protocol === "https:") return url; throw new Error("Detected an href which is not a http(s) request.");
-
Next.js公式ドキュメントを元にContent Security Policy (CSP)の適用。
- 注意点:
script
タグを利用する場合はnonce
の設定を忘れずに行う必要がある(通常パッケージマネージャーによってライブラリの導入を行うため、使う場面は限られる)。 Trusted Types
はReactの実装内で類似のことがなされるため不要。
- 注意点:
根拠
-
dangerouslySetInnerHTML
を利用しない場合、ReactがHTMLとして解釈されないようにエスケープしてくれるため。 -
href
にjavascript:脆弱性を生むコード
が指定されることは対策されないため手動でサニタイジングをする必要がある(2024年9月現在)。 -
CSPを用いることで許可されていないソースからのスクリプトの読み込みを制限でき、安全性が高まる。
仕組み化
- BiomeやESLintで
dangerouslySetInnerHTML
を利用するとCIが失敗するようにする(デフォルトの設定で有効化済)。 - 利用規約の策定 + CodeRabbit等のGPTを利用して検知。
- ヘッダーに記述する内容をテンプレート化して提供。
クロスサイトリクエストフォージェリ(CSRF)
用語説明
- CSRF: 攻撃者の罠によってWebアプリケーションの持っている機能がユーザーの意思と関係なく実行されてしまうこと。formから送信されるリクエストは同一オリジンポリシーで制限されないため生じる。
- Double submit cookie: HttpOnly属性のついていないCSRF対策専用のCookieをformを送るときに、headerに入れる。同一のtokenがformにも入れてあればOK。HttpOnly属性がついていなくても、異なるドメインからはCookieにアクセスできないため対策になる。
対策方法
- Next.js v14.0以上を利用することで特筆すべき対策は不要。ただし、Custom Route Handlers(
route.tsx
)を利用する場合は下記対策が必要になる。
基本的に不要な対策
-
バックエンドサーバーで
origin
ヘッダーを検証。 -
SameSite Cookieを設定し、クロスサイトサイトへCookieを付与しない。
-
formのPOST等で
CSRF_TOKEN
を使い、フォームの正当性の検証。 -
Double submit cookieを利用し、フォームの正当性を検証。
Set-Cookie: session=123456789abcdef; httpOnly; Secure; SameSite=Lax;
-
X-Requested-With
等の(CORS安全とされるリクエストヘッダー以外の)任意のヘッダーを付与して、そのヘッダーがリクエストに含まれているか検証。- 注意点: プリフライトリクエスト内でチェックするため、リクエスト回数が増える。他の方法がない場合に利用。
根拠
- Next.jsのv14.0以上では
Origin
ヘッダーの検証が自動で行われる。 - SameSite Cookieは最新版のChrome等のブラウザでデフォルト搭載されている。
仕組み化
- 利用規約の策定 + CodeRabbit等のGPTを利用して検知。
中間者攻撃・SSLストリッピング・(HTTP通信起因の)改ざん
用語説明
- HSTS: HTTPで初回通信しても、次からはHTTPS通信を強制する。
- max-age: HTTPS通信を行う有効期限。
- includeSubdomains: サブドメインに対してもHTTPSを強制するか。
- preload: HTTPS preloadに登録されているドメインか確認して、初回からHTTPS通信を強制する。
- サブリソース完全性: 読み込むリソースの内容のハッシュ値のbase64エンコードされたものをintegrity属性に指定することで改ざんを検知する仕組み。
対策方法
-
HSTS
を利用 or 443番ポートのみ開放。strict-transport-security: max-age=31536000; includeSubdomains; preload
基本的に不要な対策
- Mixed Contentのブロック。
- サブリソース完全性の利用。
根拠
HSTS
を利用することで、通信のHTTPS化を強制でき、中間者による改ざんによって生じる悪意あるスクリプトの埋め込みを防止できる。- Mixed Contentのブロックはブラウザでデフォルト搭載。
- ビルド時に
npm
からパッケージをインポートし、scriptタグによる読み込みをしなければ問題なし。ライブラリの脆弱性検知ツールによる対応で十分。
仕組み化
- ヘッダーに記述する内容をテンプレート化して提供。
- hstspreload.orgをチェックすることで設定を確認。
クリックジャッキング
用語説明
- クリックジャッキング: iframeで透明なボタンを配置して、ユーザーの操作を促す。
対策方法
-
X-Frame-Options
の設定。必要に応じてDENY
以外にSAMEORIGIN
等に変更する。X-Frame-Options: DENY
あるいは
Content-Security-Policy: frame-ancestors 'none'
根拠
X-Frame-Options
を設定することで、iframe
等で埋め込まれることを防止できるため。
仕組み化
- ヘッダーに記述する内容をテンプレート化して提供。
SQLインジェクション
対策方法
- Prisma等のORMの機能を利用してデータベースにアクセスし、生のSQLや
$queryRaw
、$executeRaw
を利用しない。どうしても利用する場合は、個別に調査して無害化する。 - TypedSQLの利用。
根拠
-
ORMのデフォルト機能として、SQLインジェクションの恐れのあるテキストはエスケープされるため(Prisma以外のORMの場合念の為確認が必要)。
仕組み化
- 利用規約の策定 + CodeRabbit等のGPTを利用して検知。
サイドチャネル攻撃(Spectre)
用語説明
- CORS: ブラウザ上で異なるオリジン間の安全なリソース共有を可能にする仕組み。HTTPヘッダにOriginや Access-Control-Allow-Originなどを使用して動作させる。
- 注意点: Originヘッダの値をそのままAccess-Control-Allow-Originヘッダに設定するとすべてのオリジンを許可していることと同一なため使用不可。
- サイドチャネル攻撃: CPU・メモリ等、ハードウェアの特性を悪用した攻撃。
- 例: Spectre: 高精度なタイマーを使い、徐々にメモリ内の内容を推測するもの。
- CORP: ヘッダが指定されたリソースの読み込みを同一オリジンまたは同一サイトに制限可能。
- COEP: すべてのリソースに対して、CORP or CORSヘッダを設定することを強制。
- COOP: a要素やwindow.open関数で開いたクロスオリジンのページからのアクセスを制限可能。
対策方法
-
SharedArrayBuffer
やperformance.measureMemory()
を利用しない。 -
Cross-Origin Isolation(
SharedArrayBuffer
やperformance.measureMemory()
を利用する場合のみ)(不用意に設定するとWebサイトが動かなくなることがあるため注意が必要))。-
Cross-Origin Resource Policy(CORP)
Cross-Origin-Resource-Policy: same-origin
-
Cross-Origin Embedder Policy(COEP)
Cross-Origin-Embedder-Policy: require-corp
-
Cross-Origin Opener Policy(COOP)
Cross-Origin-Opener-Policy: same-origin
- 注意点: 決済やソーシャルログイン等を利用している場合はブロックされるので
same-origin-allow-popups
を指定する。
- 注意点: 決済やソーシャルログイン等を利用している場合はブロックされるので
-
基本的に不要な対策
- Site isolation。
- Cross-Origin Resource Sharing(CORS)
根拠
- Site isolationでサイト(eTLD+1)単位でプロセスの分離、Cross-Origin Isolationでオリジンごとにプロセスの分離できるため。
- Site isolation・CORSの仕組みはデフォルトでブラウザに搭載されている。
仕組み化
- ヘッダーに記述する内容をテンプレート化して提供。
Referrerの脆弱性
対策方法
-
Referrer-Policy
の設定。Referrer-Policy: strict-origin-when-cross-origin
根拠
Referrer-Policy
を適切に設定することで、遷移先が外部サイトの場合にreferrer
を送信しなくなる。
仕組み化
- ヘッダーに記述する内容をテンプレート化して提供。
認証情報・サーバーサイドのロジックの漏洩
対策方法
- httpOnly属性の付与 or Cookieによる認証をしない(bearer token等の利用)。
import 'server-only';
の利用によるサーバーサイドのロジックの明示的な分離。- 本番環境では本番モードで起動する。
- ブラウザから見えてもいい環境変数以外の名称に
NEXT_PUBLIC_
接頭詞をつけない。
根拠
httpOnly
属性を付与することで、JavaScriptからCookieにアクセスできないようにできる。ページ遷移時やフォーム送信時等のリクエストにおいて、ブラウザはCookieを自動的にサーバへ送信するため、対策が必要。- Client Componentは開発者ツールから内部のコードが見れてしまう。
import 'server-only';
を付与することで、サーバーサイドからしかコードが実行されなくなる。 - 本番モードでは、Reactはエラーや拒否されたプロミスをクライアントに送信しないため、エラーハンドルの実装により機密情報が漏洩することは対策済み。
NEXT_PUBLIC
接頭詞をつけない環境変数をクライアントサイドから読み込むとエラーになるため、機密情報がクライアントサイドに流出することは対策可能。
仕組み化
- 利用規約の策定 + CodeRabbit等のGPTを利用して検知。
オープンリダイレクト
対策方法
- リダイレクト設定を記述する場合は、特定のURLのみリダイレクトが可能なようにバリデーションを行う。
根拠
- リダイレクト先の設定でバリデーションを行うと、悪意あるリダイレクト先は排除できる。
仕組み化
- 利用規約の策定 + CodeRabbit等のGPTを利用して検知。
その他基本的に不要な対策や技術
- Sanitize API: 同様のことをDompurifyやReactのデフォルトの機能でできるため不要とする。
チェックリスト
- データアクセス層:独立したデータアクセス層の確立された慣行があるかどうか要確認。データベースパッケージや環境変数がデータアクセス層の外でインポートされていないことを要確認。
- “use client”ファイル:コンポーネントのpropsにプライベートデータを期待していないか?型のシグネチャが過度に広範ではないか?
- “use server”ファイル:アクションの引数がアクション内、またはデータアクセス層内で検証されているか?アクション内でユーザーが再認証されているか?
-
/[param]/
:角括弧が付いているフォルダはユーザー入力を表す。パラメータが検証されているか? -
middleware.tsx
およびroute.tsx
:従来の技術を使って要追加の監査。定期的にペネトレーションテストや脆弱性スキャンを実施するか、チームのソフトウェア開発ライフサイクルに従って実施。 - Server ActionsのPropsにユーザーに操作されたくない引数を入れてないか(userIdはsessionから取得する等の方法を取る必要がある)。
-
layout
で認証チェックをしていないか。middleware
、page
、server actions
で行うべきである。
残対策集 TODO
下記はフロントエンドWebアプリケーション開発に直接関わらない箇所なため、TODOとして残している。一部はフロントエンドの高度化により対策が必要なものもあるため、現在執筆中である。
- OSコマンドインジェクション
- パス名パラメータの未チェック・ディレクトリトラバーサル
- セッション管理
- HTTPヘッダインジェクション
- メールヘッダインジェクション
- バッファオーバーフロー
- アクセス制御・認可制御の欠落