プリフライトリクエスト(Preflight Request)とは
はじめに
Webアプリケーション開発において、異なるオリジン(ドメインやポート)間でのリソース共有は日常的に行われています。しかし、セキュリティ上の理由から、ブラウザはこのような「クロスオリジンリクエスト」に制限を設けています。この制限を管理する仕組みがCORS(Cross-Origin Resource Sharing)であり、その重要な要素の一つが「プリフライトリクエスト」です。
「プリフライト」とは直訳すると「飛行前の準備」や「飛行前のチェック」の意味であり、実際のリクエストを送信する前に、そのリクエストがサーバーに受け入れられるかどうかを事前に確認するプロセスです。この確認を行うために、ブラウザは「プリフライトリクエスト」をサーバーに送信し、リクエストが許可されるかを確かめます。
本記事では、リクエストのセキュリティに関する話題の中で、特にプリフライトリクエストに焦点を当てて解説します。プリフライトリクエストがどのようにクロスオリジンリソース共有(CORS)のセキュリティをサポートするか、そしてその必要性について説明します。
調べた経緯
プリフライトリクエスト(preflight request)について調べた経緯として、Webアプリケーションの開発やAPIの実装時に、CORS(Cross-Origin Resource Sharing)ポリシーによる制限に直面した経験がきっかけです。特に、異なるオリジン(ドメインやポート)のリソースにアクセスする際に、クライアント側からサーバーに送るリクエストに関してどのようにCORSが適用されるかを理解する必要がありました。プリフライトリクエストはそのプロセスの一部であり、リソース間で安全に通信できるように設計されています。
OPTIONSリクエストがなぜあるのか
OPTIONS リクエストは、CORSの制限により、ブラウザがサーバーに対してリクエストが許可されているかを事前に確認するために送信されます。通常、これらは「プリフライトリクエスト」と呼ばれ、実際のリクエストが送られる前にサーバーがそのリクエストを許可するかどうかを判定します。
たとえば、特定のHTTPメソッド(PUT, DELETE, PATCHなど)やカスタムヘッダーを使う場合、サーバーがそのリクエストを処理できるかを確認するためにOPTIONSメソッドで事前に確認します。これにより、サーバー側で予期しないリクエストを拒否したり、誤ったリクエストが送信されるのを防ぐことができます。
プリフライトリクエストがないとどうなるのか
もしプリフライトリクエストがない場合、次の問題が発生する可能性があります:
- サーバーが許可していないメソッドやヘッダーが送信され、意図しない動作やセキュリティのリスクが発生する可能性がある
- クロスオリジンリクエストが正常に処理されず、リクエストがブロックされることで、Webアプリケーションが期待通りに動作しないことがある
- ブラウザは、サーバーがリクエストを処理できるか確認する手段がなくなり、エラーを表示したり、リクエストを完全にブロックしたりする
プリフライトがあるリクエストとないリクエストについて
プリフライトリクエストはすべてのクロスオリジンリクエストに必要なわけではありません。以下のような条件に応じて、プリフライトリクエストが発生するかどうかが決まります:
プリフライトが発生するリクエスト
- PUT, DELETE, PATCH などのHTTPメソッドを使う場合
- カスタムヘッダー(X-Custom-Header など)を使用する場合
- コンテンツタイプが application/json や application/xml の場合
- リクエストが「安全でない」メソッドを使用する場合(例:非簡易リクエスト)
プリフライトが発生しないリクエスト
- 単純なGET, POSTリクエスト
- 特定のヘッダー(Content-Type が application/x-www-form-urlencoded、multipart/form-data、text/plainの場合)を使ったリクエスト
- クロスオリジンでもサーバーが適切に設定されている場合
プリフライトリクエストの実際の流れ
実際のプリフライトリクエストの流れを見てみましょう:
- ブラウザがクロスオリジンリクエストを検出
- 「単純」ではないリクエストの場合、OPTIONSメソッドを使用してプリフライトリクエストを送信
- サーバーは以下のようなレスポンスヘッダーを返す:
-
Access-Control-Allow-Origin
: 許可されるオリジン -
Access-Control-Allow-Methods
: 許可されるHTTPメソッド -
Access-Control-Allow-Headers
: 許可されるヘッダー
-
- ブラウザはレスポンスを評価し、許可されていれば本来のリクエストを送信
- 許可されていない場合は、エラーを発生させてリクエストをブロック
サーバー側での対応方法
サーバー側でプリフライトリクエストに適切に対応するには、以下のようなヘッダーを設定する必要があります:
app.use((req, res, next) => {
// 許可するオリジンを指定
res.header('Access-Control-Allow-Origin', 'https://example.com');
// 許可するHTTPメソッドを指定
res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
// 許可するヘッダーを指定
res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization');
// プリフライトリクエストのキャッシュ時間(秒)
res.header('Access-Control-Max-Age', '86400');
// OPTIONSリクエストに対しては、ここで処理を終了
if (req.method === 'OPTIONS') {
return res.sendStatus(200);
}
next();
});
プリフライトリクエストのデバッグ方法
プリフライトリクエストに関する問題をデバッグする方法は以下の通りです
- ブラウザの開発者ツールのネットワークタブを開く
- OPTIONSリクエストとそのレスポンスヘッダーを確認する
- CORSエラーメッセージを確認する(通常コンソールに表示される)
- サーバー側のログを確認し、プリフライトリクエストが正しく処理されているか確認する
一般的なCORSエラーには以下のようなものがあります
- Access-Control-Allow-Origin ヘッダーが適切に設定されていない
- リクエストで使用しているメソッドが Access-Control-Allow-Methods に含まれていない
- カスタムヘッダーが Access-Control-Allow-Headers に含まれていない
まとめ
プリフライトリクエストは、クロスオリジンでのやり取りを安全にするために大事な仕組みです。特に、PUTやDELETEのような「危険な」HTTPメソッドやカスタムヘッダーを使うときには、サーバーがそのリクエストを受け入れていいか確認するために必要です。
しっかりとサーバー側で設定しておけば、クロスオリジンのリクエストがスムーズに処理され、アプリケーションもちゃんと動きます。セキュリティと利便性のバランスを取るために、CORS設定はきちんと行うことが大切だなと感じました。
Discussion