NginxでのFastCGIプロキシの理解と実装

提供:Dev Guides
移動先:案内検索

序章

Nginxは、利用可能な最も柔軟で強力なWebサーバーソリューションの1つになりました。 ただし、設計の観点からは、何よりもまずプロキシサーバーです。 この焦点は、他のサーバーでリクエストを処理するときにNginxが非常にパフォーマンスが高いことを意味します。

Nginxは、http、FastCGI、uwsgi、SCGI、またはmemcachedを使用してリクエストをプロキシできます。 このガイドでは、最も一般的なプロキシプロトコルの1つであるFastCGIプロキシについて説明します。

FastCGIプロキシを使用する理由

Nginx内のFastCGIプロキシは、通常、クライアント要求を直接処理しない、または処理すべきでないアプリケーションサーバーに対するクライアント要求を変換するために使用されます。 FastCGIは、以前のCGI、つまりCommon Gateway Interfaceに基づくプロトコルであり、各要求を個別のプロセスとして実行しないことでパフォーマンスを向上させることを目的としています。 これは、動的コンテンツの要求を処理するサーバーと効率的にインターフェースするために使用されます。

Nginx内でのFastCGIプロキシの主なユースケースの1つは、PHP処理です。 mod_phpモジュールを使用してPHP処理を直接処理できるApacheとは異なり、NginxはPHPリクエストを処理するために別のPHPプロセッサに依存する必要があります。 ほとんどの場合、この処理はphp-fpmで処理されます。これは、Nginxで動作するように広範囲にテストされたPHPプロセッサです。

FastCGIを使用するNginxは、FastCGI要求に応答するように構成されたアクセス可能なコンポーネントがある限り、他の言語を使用するアプリケーションで使用できます。

FastCGIプロキシの基本

一般に、リクエストのプロキシには、プロキシサーバー(この場合はNginx)が関与し、クライアントからバックエンドサーバーにリクエストを転送します。 NginxがFastCGIプロトコルを使用してプロキシする実際のサーバーを定義するために使用するディレクティブはfastcgi_passです。

たとえば、PHPに対する一致するリクエストを、FastCGIプロトコルを使用したPHP処理の処理専用のバックエンドに転送するには、基本的なロケーションブロックは次のようになります。

# server context

location ~ \.php$ {
    fastcgi_pass 127.0.0.1:9000;
}

. . .

上記のスニペットは、情報が少なすぎるため、実際にはそのままでは機能しません。 プロキシ接続が確立されるたびに、プロキシされたリクエストがバックエンドサーバーにとって意味のあるものになるように、元のリクエストを変換する必要があります。 FastCGIパスを使用してプロトコルを変更しているため、これには追加の作業が必要です。

http-to-httpプロキシでは、主にhttpヘッダーを拡張して、バックエンドがクライアントに代わってプロキシサーバーに応答するために必要な情報を確実に取得できるようにしますが、FastCGIは、httpヘッダーを読み取ることができない別個のプロトコルです。 この考慮事項により、関連情報は他の方法でバックエンドに渡す必要があります。

FastCGIプロトコルを使用するときに追加情報を渡す主な方法は、パラメーターを使用することです。 バックグラウンドサーバーは、これらを読み取って処理するように構成し、検出した内容に応じて動作を変更する必要があります。 Nginxは、fastcgi_paramディレクティブを使用してFastCGIパラメーターを設定できます。

PHPのFastCGIプロキシシナリオで実際に機能する最低限の構成は、次のようなものです。

# server context

location ~ \.php$ {
    fastcgi_param REQUEST_METHOD $request_method;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    fastcgi_pass 127.0.0.1:9000;
}

. . .

上記の構成では、REQUEST_METHODおよびSCRIPT_FILENAMEと呼ばれる2つのFastCGIパラメーターを設定します。 これらは両方とも、バックエンドサーバーがリクエストの性質を理解するために必要です。 前者は実行する必要のある操作のタイプを指示し、後者は実行するファイルをアップストリームに指示します。

この例では、いくつかのNginx変数を使用して、これらのパラメーターの値を設定しました。 $request_method変数には、常にクライアントから要求されたhttpメソッドが含まれます。

SCRIPT_FILENAMEパラメーターは、$document_root変数と$fastcgi_script_name変数の組み合わせに設定されます。 $document_rootには、rootディレクティブで設定されたベースディレクトリへのパスが含まれます。 $fastcgi_script_name変数がリクエストURIに設定されます。 リクエストURIがスラッシュ(/)で終わる場合、fastcgi_indexディレクティブの値が末尾に追加されます。 Nginxインスタンスと同じマシンでFastCGIプロセッサを実行しているため、このタイプの自己参照ロケーション定義が可能です。

別の例を見てみましょう:

# server context
root /var/www/html;

location /scripts {
    fastcgi_param REQUEST_METHOD $request_method;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    fastcgi_index index.php;
    fastcgi_pass unix:/var/run/php5-fpm.sock;
}

. . .

上記の場所が/scripts/test/のリクエストを処理するために選択された場合、SCRIPT_FILENAMEの値は、rootディレクティブ、リクエストURI、およびfastcgi_indexディレクティブ。 この例では、パラメーターは/var/www/html/scripts/test/index.phpに設定されます。

ネットワークソケットの代わりにUnixソケットを使用してFastCGIバックエンドを指定したという点で、上記の構成にもう1つの重要な変更を加えました。 Nginxは、どちらのタイプのインターフェースを使用してFastCGIアップストリームに接続できます。 FastCGIプロセッサが同じホスト上にある場合は、通常、セキュリティのためにUnixソケットをお勧めします。

FastCGI構成の分割

保守可能なコードの重要なルールは、DRY(「Do n'tRepeatYourself」)の原則に従おうとすることです。 これにより、エラーが減少し、再利用性が向上し、整理が容易になります。 Nginxを管理するための主要な推奨事項の1つは、常に最も広い適用範囲でディレクティブを設定することであることを考慮すると、これらの基本的な目標はNginx構成にも適用されます。

FastCGIプロキシ構成を処理する場合、ほとんどの使用インスタンスは構成の大部分を共有します。 このため、およびNginx継承モデルが機能する方法のために、ほとんどの場合、一般的なスコープでパラメーターを宣言することが有利です。

親コンテキストでのFastCGI構成の詳細の宣言

繰り返しを減らす1つの方法は、より高い親コンテキストで構成の詳細を宣言することです。 実際のfastcgi_pass以外のすべてのパラメーターは、より高いレベルで指定できます。 それらは、パスが発生する場所に下向きにカスケードされます。 これは、複数の場所で同じ構成を使用できることを意味します。

たとえば、上記のセクションの最後の構成スニペットを変更して、複数の場所で役立つようにすることができます。

# server context
root /var/www/html;

fastcgi_param REQUEST_METHOD $request_method;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_index index.php;

location /scripts {
    fastcgi_pass unix:/var/run/php5-fpm.sock;
}

location ~ \.php$ {
    fastcgi_pass 127.0.0.1:9000;
}

. . .

上記の例では、fastcgi_param宣言とfastcgi_indexディレクティブの両方が、後続の両方のロケーションブロックで使用できます。 これは、繰り返しの宣言を削除する1つの方法です。

ただし、上記の構成には1つの重大な欠点があります。 下位コンテキストでfastcgi_paramが宣言されている場合、親コンテキストのfastcgi_param値のnoneが継承されます。 継承された値をのみ使用するか、いずれも使用しません。

fastcgi_paramディレクティブは、Nginx用語ではarrayディレクティブです。 ユーザーの観点からは、配列ディレクティブは基本的に、単一のコンテキストで複数回使用できるディレクティブです。 後続の各宣言は、Nginxが前の宣言から知っていることに新しい情報を追加します。 fastcgi_paramディレクティブは、ユーザーが複数のパラメーターを設定できるようにするために、配列ディレクティブとして設計されました。

配列ディレクティブは、他のディレクティブとは異なる方法で子コンテキストを継承します。 配列ディレクティブからの情報は、子コンテキストのどの場所にも存在しない場合にのみ、子コンテキストに継承されます。 これは、場所内でfastcgi_paramを使用すると、親コンテキストから継承された値を効果的に完全にクリアすることを意味します。

たとえば、上記の構成を少し変更できます。

# server context
root /var/www/html;

fastcgi_param REQUEST_METHOD $request_method;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_index index.php;

location /scripts {
    fastcgi_pass unix:/var/run/php5-fpm.sock;
}

location ~ \.php$ {
    fastcgi_param QUERY_STRING $query_string;
    fastcgi_pass 127.0.0.1:9000;
}

. . .

一見すると、REQUEST_METHODおよびSCRIPT_FILENAMEパラメーターが2番目のロケーション・ブロックに継承され、QUERY_STRINGパラメーターがその特定のコンテキストで追加で使用可能になると思われるかもしれません。

実際に発生するのは、親のfastcgi_param値のall が2番目のコンテキストで消去され、QUERY_STRINGパラメーターのみが設定されることです。 REQUEST_METHODおよびSCRIPT_FILENAMEパラメーターは未設定のままになります。

同じコンテキスト内のパラメーターの複数の値に関する注意

この時点で間違いなく言及する価値のあることの1つは、単一のコンテキスト内で同じパラメーターに複数の値を設定することの意味です。 次の例を議論のポイントとして取り上げましょう。

# server context

location ~ \.php$ {
    fastcgi_param REQUEST_METHOD $request_method;
    fastcgi_param SCRIPT_FILENAME $request_uri;

    fastcgi_param DOCUMENT_ROOT initial;
    fastcgi_param DOCUMENT_ROOT override;

    fastcgi_param TEST one;
    fastcgi_param TEST two;
    fastcgi_param TEST three;

    fastcgi_pass 127.0.0.1:9000;
}

. . .

上記の例では、TESTおよびDOCUMENT_ROOTパラメーターを単一のコンテキスト内で複数回設定しています。 fastcgi_paramは配列ディレクティブであるため、後続の各宣言はNginxの内部レコードに追加されます。 TESTパラメーターには、onetwo、およびthreeに設定する宣言が配列に含まれます。

この時点で理解することが重要なのは、これらすべてがNginxからの追加処理なしでFastCGIバックエンドに渡されるということです。 これは、これらの値の処理方法を決定するのは、選択したFastCGIプロセッサ次第であることを意味します。 残念ながら、異なるFastCGIプロセッサは、渡された値を完全に異なる方法で処理します

たとえば、上記のパラメーターがPHP-FPMによって受信された場合、final値は以前の値のいずれかをオーバーライドするように解釈されます。 したがって、この場合、TESTパラメーターはthreeに設定されます。 同様に、DOCUMENT_ROOTパラメーターはoverrideに設定されます。

ただし、上記の値がFsgiWrapなどに渡された場合、値の解釈は大きく異なります。 まず、スクリプトの実行に使用する値を決定するための初期パスを作成します。 initialDOCUMENT_ROOT値を使用してスクリプトを検索します。 ただし、実際のパラメーターをスクリプトに渡すときは、PHP-FPMと同様に、最終的な値を渡します。

この不整合と予測不可能性は、同じパラメーターを複数回設定するときに、バックエンドに依存して意図を正しく解釈することはできず、またそうすべきではないことを意味します。 唯一の安全な解決策は、各パラメーターを1回だけ宣言することです。 これは、fastcgi_paramディレクティブでデフォルト値を安全にオーバーライドするようなことはないことも意味します。

インクルードを使用して別のファイルからFastCGI構成をソースする

一般的な構成アイテムを分離する別の方法があります。 includeディレクティブを使用して、別のファイルの内容をディレクティブ宣言の場所に読み込むことができます。

これは、すべての一般的な構成アイテムを1つのファイルに保持し、必要な構成の任意の場所に含めることができることを意味します。 Nginxはincludeが呼び出された場所に実際のファイルの内容を配置するため、親コンテキストから子に下向きに継承することはありません。 これにより、fastcgi_paramの値が消去されなくなり、必要に応じて追加のパラメーターを設定できるようになります。

まず、共通のFastCGI構成値を構成ディレクトリ内の別のファイルに設定できます。 このファイルをfastcgi_commonと呼びます。

fastcgi_param REQUEST_METHOD $request_method;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;

これで、これらの構成値を使用する場所ならどこでも、このファイルを読み取ることができます。

# server context
root /var/www/html;

location /scripts {
    include fastcgi_common;

    fastcgi_index index.php;
    fastcgi_pass unix:/var/run/php5-fpm.sock;
}

location ~ \.php$ {
    include fastcgi_common;
    fastcgi_param QUERY_STRING $query_string;
    fastcgi_param CONTENT_TYPE $content_type;
    fastcgi_param CONTENT_LENGTH $content_length;

    fastcgi_index index.php;
    fastcgi_pass 127.0.0.1:9000;
}

. . .

ここでは、いくつかの一般的なfastcgi_param値を、デフォルトのNginx構成ディレクトリにあるfastcgi_commonというファイルに移動しました。 次に、宣言された値を挿入するときに、そのファイルをソースします。

この構成について注意すべき点がいくつかあります。

まず、ソースを作成する予定のファイルに、場所ごとにカスタマイズしたい値を配置しなかったということです。 同じパラメータに複数の値を設定するときに発生する前述の解釈の問題と、非配列ディレクティブはコンテキストごとに1回しか設定できないため、変更したくない共通ファイルのみを配置します。 コンテキストごとにカスタマイズしたいすべてのディレクティブ(またはパラメーターキー)は、共通ファイルから除外する必要があります。

お気づきかもしれませんが、2番目のロケーションブロックにいくつかの追加のFastCGIパラメーターを設定しています。 これが私たちが達成したいと思っていた能力です。 共通の値を消去することなく、必要に応じて追加のfastcgi_paramパラメーターを設定することができました。

fastcgi_paramsファイルまたはfastcgi.confファイルの使用

上記の戦略を念頭に置いて、Nginx開発者と多くの配布パッケージングチームは、FastCGIパスの場所に含めることができる共通のパラメーターの適切なセットを提供することに取り組んできました。 これらはfastcgi_paramsまたはfastcgi.confと呼ばれます。

これらの2つのファイルはほぼ同じですが、実際には、単一のパラメーターに複数の値を渡すことについて前に説明した問題の結果が唯一の違いです。 fastcgi_paramsファイルには、SCRIPT_FILENAMEパラメーターの宣言が含まれていませんが、fastcgi.confファイルには含まれています。

fastcgi_paramsファイルはずっと長い間利用可能でした。 fastcgi_paramsに依存する構成の破損を回避するために、SCRIPT_FILENAMEのデフォルト値を提供することが決定された場合、新しいファイルを作成する必要がありました。 そうしないと、そのパラメーターが共通ファイルとFastCGIパスの場所の両方に設定される可能性があります。 これについては、MartinFjordvaldによるこれら2つのファイルの履歴に関する優れた投稿で詳しく説明されています。

人気のあるディストリビューションの多くのパッケージメンテナは、これらのファイルの1つだけを含めるか、コンテンツを正確にミラーリングすることを選択しました。 これらのうち1つしか使用できない場合は、使用しているものを使用してください。 ニーズに合わせて自由に変更してください。

これらのファイルの両方を使用できる場合は、ほとんどのFastCGIパスの場所で、SCRIPT_FILENAMEパラメーターの宣言が含まれているため、fastcgi.confファイルを含めることをお勧めします。 これは通常望ましいことですが、この値をカスタマイズしたい場合もあります。

これらは、ルートNginx構成ディレクトリを基準にした場所を参照することで含めることができます。 Nginxがパッケージマネージャーとともにインストールされている場合、ルートNginx構成ディレクトリは通常/etc/nginxのようなものです。

次のようなファイルを含めることができます。

# server context

location ~ \.php$ {
    include fastcgi_params;
    # You would use "fastcgi_param SCRIPT_FILENAME . . ." here afterwards
    
    . . .

}

またはこのように:

# server context

location ~ \.php$ {
    include fastcgi.conf;

    . . .

}

重要なFastCGIディレクティブ、パラメーター、および変数

上記のセクションでは、他の概念を示す手段として、かなりの数のパラメーターを、多くの場合Nginx変数に設定しました。 また、あまり説明せずにいくつかのFastCGIディレクティブを導入しました。 このセクションでは、設定する一般的なディレクティブのいくつか、変更する必要のあるパラメーター、および必要な情報を含む可能性のあるいくつかの変数について説明します。

一般的なFastCGIディレクティブ

以下は、FastCGIパスを操作するための最も有用なディレクティブのいくつかを表しています。

  • fastcgi_pass :現在のコンテキストでリクエストをバックエンドに渡す実際のディレクティブ。 これは、FastCGIプロセッサに到達できる場所を定義します。
  • fastcgi_param :パラメーターを値に設定するために使用できる配列ディレクティブ。 ほとんどの場合、これはNginx変数と組み合わせて使用され、FastCGIパラメーターをリクエストに固有の値に設定します。
  • try_files :FastCGI固有のディレクティブではありませんが、FastCGIパスの場所で使用される一般的なディレクティブです。 これは、要求されたファイルがFastCGIプロセッサに渡される前に存在することを確認するために、要求サニテーションルーチンの一部としてよく使用されます。
  • include :繰り返しますが、FastCGI固有のディレクティブではありませんが、FastCGIパスコンテキストで頻繁に使用されるディレクティブです。 ほとんどの場合、これは、複数の場所に共通の共有構成の詳細を含めるために使用されます。
  • fastcgi_split_path_info :このディレクティブは、2つのキャプチャされたグループを持つ正規表現を定義します。 最初にキャプチャされたグループは、$fastcgi_script_name変数の値として使用されます。 2番目にキャプチャされたグループは、$fastcgi_path_info変数の値として使用されます。 これらは両方とも、要求を正しく解析するためによく使用されます。これにより、プロセッサは、要求のどの部分が実行するファイルであり、どの部分がスクリプトに渡す追加情報であるかを知ることができます。
  • fastcgi_index :これは、スラッシュ(/)で終わる$fastcgi_script_name値に追加する必要があるインデックスファイルを定義します。 これは、SCRIPT_FILENAMEパラメーターが$document_root$fastcgi_script_nameに設定されていて、ファイルの後に情報を含む要求を受け入れるようにロケーションブロックが構成されている場合に役立つことがよくあります。
  • fastcgi_intercept_errors :このディレクティブは、FastCGIサーバーから受信したエラーをNginxで処理するか、クライアントに直接渡すかを定義します。

上記のディレクティブは、一般的なFastCGIパスを設計するときに使用するもののほとんどを表しています。 これらすべてを常に使用するわけではありませんが、次に説明するFastCGIパラメーターおよび変数と非常に密接に相互作用することがわかります。

FastCGIで使用される一般的な変数

FastCGIパスで使用する可能性のあるパラメーターについて説明する前に、これらのパラメーターの設定で利用する一般的なNginx変数について少し説明する必要があります。 これらのいくつかはNginxのFastCGIモジュールによって定義されていますが、ほとんどはCoreモジュールからのものです。

  • $query_stringまたは$args:元のクライアント要求で指定された引数。
  • $ is_args :「?」に等しくなりますリクエストに引数があり、それ以外の場合は空の文字列に設定されます。 これは、引数を持つ場合と持たない場合があるパラメーターを作成するときに役立ちます。
  • $ request_method :これは元のクライアント要求メソッドを示します。 これは、現在のコンテキスト内で操作を許可する必要があるかどうかを判断するのに役立ちます。
  • $ content_type :これはContent-Typeリクエストヘッダーに設定されます。 この情報は、後続のコンテンツを正しく処理するためにユーザーの要求がPOSTである場合に、プロキシによって必要になります。
  • $ content_length :これはクライアントからのContent-Lengthヘッダーの値に設定されます。 この情報は、クライアントのPOST要求に必要です。
  • $ fastcgi_script_name :これには実行するスクリプトファイルが含まれます。 リクエストがスラッシュ(/)で終了する場合、fastcgi_indexディレクティブの値が末尾に追加されます。 fastcgi_split_path_infoディレクティブが使用されている場合、この変数は、そのディレクティブによって定義された最初にキャプチャされたグループに設定されます。 この変数の値は、実行される実際のスクリプトを示している必要があります。
  • $ request_filename :この変数には、要求されたファイルのファイルパスが含まれます。 rootaliasディレクティブの両方、および$fastcgi_script_nameの値を考慮して、現在のドキュメントルートの値を取得することでこの値を取得します。 これは、SCRIPT_FILENAMEパラメーターを割り当てる非常に柔軟な方法です。
  • $ request_uri :クライアントから受信したリクエスト全体。 これには、スクリプト、追加のパス情報、およびクエリ文字列が含まれます。
  • $ fastcgi_path_info :この変数には、リクエストのスクリプト名の後に使用できる追加のパス情報が含まれています。 この値には、実行するスクリプトが知っておくべき別の場所が含まれている場合があります。 この変数は、fastcgi_split_path_infoディレクティブを使用するときに、2番目にキャプチャされた正規表現グループから値を取得します。
  • $ document_root :この変数には、現在のドキュメントルート値が含まれます。 これは、rootまたはaliasディレクティブに従って設定されます。
  • $ uri :この変数には、正規化が適用された現在のURIが含まれています。 書き換えまたは内部リダイレクトする特定のディレクティブはURIに影響を与える可能性があるため、この変数はそれらの変更を表現します。

ご覧のとおり、FastCGIパラメーターの設定方法を決定する際に使用できる変数はかなりあります。 これらの多くは似ていますが、スクリプトの実行に影響を与える微妙な違いがいくつかあります。

一般的なFastCGIパラメータ

FastCGIパラメータは、リクエストの送信先であるFastCGIプロセッサで利用できるようにしたいキー値情報を表します。 すべてのアプリケーションが同じパラメータを必要とするわけではないため、多くの場合、アプリのドキュメントを参照する必要があります。

これらのパラメーターのいくつかは、プロセッサーが実行するスクリプトを正しく識別するために必要です。 その他はスクリプトで使用できるようになり、設定されたパラメーターに依存するように構成されている場合は、スクリプトの動作が変更される可能性があります。

  • QUERY_STRING :このパラメーターは、クライアントから提供された任意のクエリ文字列に設定する必要があります。 これは通常、「?」の後に指定されたキーと値のペアになります。 URIで。 通常、このパラメーターは$query_stringまたは$args変数のいずれかに設定され、どちらにも同じデータが含まれている必要があります。
  • REQUEST_METHOD :このパラメーターは、クライアントによって要求されたアクションのタイプをFastCGIプロセッサーに示します。 これは、パスが正しく機能するために設定する必要がある数少ないパラメーターの1つです。
  • CONTENT_TYPE :上記で設定したリクエスト方式が「POST」の場合、このパラメータを設定する必要があります。 FastCGIプロセッサが期待するコンテンツのタイプを示します。 これはほとんどの場合、元のリクエストの情報に従って設定される$content_type変数に設定されます。
  • CONTENT_LENGTH :リクエストメソッドが「POST」の場合、このパラメータを設定する必要があります。 これはコンテンツの長さを示します。 これはほとんどの場合、$content_lengthに設定されます。これは、元のクライアント要求の情報から値を取得する変数です。
  • SCRIPT_NAME :このパラメーターは、実行されるメインスクリプトの名前を示すために使用されます。 これは非常に重要なパラメータであり、ニーズに応じてさまざまな方法で設定できます。 多くの場合、これは$fastcgi_script_nameに設定されます。これは、リクエストURI、スラッシュで終わる場合はfastcgi_indexが追加されたリクエストURI、または[を使用する場合は最初にキャプチャされたグループです。 X176X]。
  • SCRIPT_FILENAME :このパラメーターは、実行するスクリプトのディスク上の実際の場所を指定します。 SCRIPT_NAMEパラメーターとの関係のため、一部のガイドでは$document_root$fastcgi_script_nameの使用を推奨しています。 多くの利点があるもう1つの方法は、$request_filenameを使用することです。
  • REQUEST_URI :これには、実行するスクリプト、追加のパス情報、および引数を含む、変更されていない完全なリクエストURIが含まれている必要があります。 一部のアプリケーションは、この情報を自分で解析することを好みます。 このパラメーターは、それを行うために必要な情報を提供します。
  • PATH_INFO :PHP構成ファイルでcgi.fix_pathinfoが「1」に設定されている場合、スクリプト名の後に追加された追加のパス情報が含まれます。 これは、スクリプトが作用するファイル引数を定義するためによく使用されます。 cgi.fix_pathinfoを「1」に設定すると、スクリプト要求が他の手段でサニタイズされない場合、セキュリティに影響を与える可能性があります(これについては後で説明します)。 これは、$fastcgi_path_info変数に設定される場合があります。この変数には、fastcgi_split_path_infoディレクティブから2番目にキャプチャされたグループが含まれます。 また、一時変数を使用する必要がある場合もあります。これは、その値が他の処理によって破壊されることがあるためです。
  • PATH_TRANSLATED :このパラメーターは、PATH_INFOに含まれるパス情報を実際のファイルシステムパスにマップします。 通常、これは$document_root$fastcgi_path_infoのように設定されますが、上記のように、後の変数を一時的に保存された変数に置き換える必要がある場合もあります。

FastCGIに渡す前にリクエストを確認する

まだ取り上げていない非常に重要なトピックの1つは、動的リクエストをアプリケーションサーバーに安全に渡す方法です。 有効性に関係なく、すべてのリクエストをバックエンドアプリケーションに渡すことは、非効率的であるだけでなく、危険でもあります。 攻撃者がサーバーに任意のコードを実行させようとして、悪意のあるリクエストを作成する可能性があります。

この問題に対処するには、正当な要求のみをFastCGIプロセッサに送信していることを確認する必要があります。 これは、特定のセットアップのニーズや、FastCGIプロセッサがNginxインスタンスと同じシステム上にあるかどうかに応じて、さまざまな方法で実行できます。

構成を設計する方法を通知する必要がある基本的なルールの1つは、ユーザーファイルの処理と解釈を決して許可しないことです。 悪意のあるユーザーが、画像など、一見無害に見えるファイルに有効なコードを埋め込むのは比較的簡単です。 このようなファイルがサーバーにアップロードされたら、FastCGIプロセッサにファイルが送信されないようにする必要があります。

ここで解決しようとしている主な問題は、CGI仕様で実際に指定されている問題です。 この仕様では、実行するスクリプトファイルを指定し、その後にスクリプトで使用できる追加のパス情報を指定できます。 この実行モデルでは、ユーザーは正当なスクリプトのように見えるURIを要求できますが、実行される実際の部分はパスの早い段階にあります。

/test.jpg/index.phpのリクエストを考えてみましょう。 構成が.phpで終わるすべての要求を、その正当性をテストせずに単にプロセッサーに渡す場合、プロセッサーは、仕様に従っている場合、その場所をチェックし、可能であればそれを実行します。 がファイルを検出しない場合は、仕様に従い、[X195X] ファイルの実行を試み、スクリプトの追加パス情報として/index.phpをマークします。 ご覧のとおり、これは、ユーザーアップロードのアイデアと組み合わせると、非常に望ましくない結果をもたらす可能性があります。

この問題を解決するには、さまざまな方法があります。 アプリケーションが処理のためにこの追加のパス情報に依存しない場合、最も簡単なのは、プロセッサでそれをオフにすることです。 PHP-FPMの場合、php.iniファイルでこれをオフにすることができます。 たとえば、Ubuntuシステムでは、次のファイルを編集できます。

sudo nano /etc/php5/fpm/php.ini

cgi.fix_pathinfoオプションを検索し、コメントを外して「0」に設定すると、この「機能」が無効になります。

cgi.fix_pathinfo=0

PHP-FPMプロセスを再起動して、変更を加えます。

sudo service php5-fpm restart

これにより、PHPはパスの最後のコンポーネントでのみ実行を試みます。 したがって、上記の例では、/test.jpg/index.phpファイルが存在しない場合、PHPは/test.jpgを実行しようとする代わりに、正しくエラーになります。

FastCGIプロセッサがNginxインスタンスと同じマシン上にある場合の別のオプションは、プロセッサに渡す前に、ディスク上のファイルの存在を確認することです。 /test.jgp/index.phpファイルが存在しない場合は、エラーになります。 含まれている場合は、処理のためにバックエンドに送信します。 これにより、実際には、上記とほぼ同じ動作が発生します。

location ~ \.php$ {
        try_files $uri =404;

        . . .

}

アプリケーションがパス情報の動作に依存して正しい解釈を行う場合でも、リクエストをバックエンドに送信するかどうかを決定する前にチェックを行うことで、この動作を安全に許可できます。

たとえば、信頼できないアップロードを許可するディレクトリを具体的に照合し、それらがプロセッサに渡されないようにすることができます。 たとえば、アプリケーションのアップロードディレクトリが/uploads/の場合、正規表現が評価される前に一致する次のようなロケーションブロックを作成できます。

location ^~ /uploads {
}

内部では、PHPファイルのあらゆる種類の処理を無効にすることができます。

location ^~ /uploads {
    location ~* \.php$ { return 403; }
}

親の場所は、/uploadsで始まるすべてのリクエストと一致し、PHPファイルを処理するリクエストは、バックエンドに送信する代わりに403エラーを返します。

fastcgi_split_path_infoディレクティブを使用して、スクリプトとして解釈する必要があるリクエストの部分と、正規表現を使用して追加のパス情報として定義する必要がある部分を手動で定義することもできます。 これにより、パス情報機能に依存することができますが、スクリプトと見なすものとパスと見なすものを正確に定義できます。

たとえば、.phpで終わるパスコンポーネントの最初のインスタンスを実行するスクリプトと見なすロケーションブロックを設定できます。 残りは追加のパス情報と見なされます。 これは、/test.jpg/index.phpのリクエストの場合、追加のパス情報なしで、パス全体をスクリプト名としてプロセッサに送信できることを意味します。

この場所は次のようになります。

location ~ [^/]\.php(/|$) {

    fastcgi_split_path_info ^(.+?\.php)(.*)$;
    set $orig_path $fastcgi_path_info;

    try_files $fastcgi_script_name =404;

    fastcgi_pass unix:/var/run/php5-fpm.sock;
    fastcgi_index index.php;
    include fastcgi_params;

    fastcgi_param SCRIPT_FILENAME $request_filename;
    fastcgi_param PATH_INFO $orig_path;
    fastcgi_param PATH_TRANSLATED $document_root$orig_path;
}

上記のブロックは、cgi.fix_pathinfoが「1」に設定されているPHP構成で機能し、追加のパス情報を許可します。 ここで、ロケーションブロックは、.phpで終わるリクエストだけでなく、追加のディレクトリコンポーネントを示すスラッシュ(/)の直前の.phpで終わるリクエストにも一致します。

ブロック内で、fastcgi_split_path_infoディレクティブは、正規表現を使用して2つのキャプチャされたグループを定義します。 最初のグループは、URIの最初から.phpの最初のインスタンスまでの部分と一致し、それを$fastcgi_script_name変数に配置します。 次に、その時点以降の情報を2番目にキャプチャされたグループに配置し、$fastcgi_path_infoという変数に格納します。

setディレクティブを使用して、この時点で$fastcgi_path_infoに保持されている値を$orig_pathという変数に格納します。 これは、$fastcgi_path_info変数がtry_filesディレクティブによってすぐに消去されるためです。

try_filesを使用して、上記でキャプチャしたスクリプト名をテストします。 これは、実行しようとしているスクリプトがディスク上にあることを確認するファイル操作です。 ただし、これには$fastcgi_path_info変数をクリアするという副作用もあります。

従来のFastCGIパスを実行した後、通常どおりSCRIPT_FILENAMEを設定します。 また、PATH_INFO$orig_path変数にオフロードした値に設定します。 $fastcgi_path_infoはクリアされましたが、元の値はこの変数に保持されます。 また、PATH_TRANSLATEDパラメーターを設定して、追加のパス情報をディスク上の存在する場所にマップします。 これを行うには、$document_root変数を$orig_path変数と組み合わせます。

これにより、/index.php/users/viewのようなリクエストを作成して、/index.phpファイルが/users/viewディレクトリに関する情報を処理できるようにすると同時に、/test.jpg/index.phpが実行される状況を回避できます。 スクリプトは常に.phpで終わる最短のコンポーネントに設定されるため、この問題は回避されます。

スクリプトファイルの場所を変更する必要がある場合は、エイリアスディレクティブを使用してこれを機能させることもできます。 これは、ロケーションヘッダーとfastcgi_split_path_info定義の両方で説明する必要があります。

location ~ /test/.+[^/]\.php(/|$) {

    alias /var/www/html;

    fastcgi_split_path_info ^/test(.+?\.php)(.*)$;
    set $orig_path $fastcgi_path_info;

    try_files $fastcgi_script_name =404;

    fastcgi_pass unix:/var/run/php5-fpm.sock;
    fastcgi_index index.php;
    include fastcgi_params;

    fastcgi_param SCRIPT_FILENAME $request_filename;
    fastcgi_param PATH_INFO $orig_path;
    fastcgi_param PATH_TRANSLATED $document_root$orig_path;
}

これらにより、PATH_INFOパラメーターを使用するアプリケーションを安全に実行できます。 これを正しく機能させるには、php.iniファイルのcgi.fix_pathinfoオプションを「1」に変更する必要があることを忘れないでください。 php-fpm.confファイルのsecurity.limit_extensionsをオフにする必要がある場合もあります。

結論

うまくいけば、これでNginxのFastCGIプロキシ機能について理解を深めることができます。 この機能により、Nginxは、動的コンテンツの責任をより適切なソフトウェアにオフロードしながら、高速接続処理と静的コンテンツの提供という強みを発揮できます。 FastCGIを使用すると、Nginxは、パフォーマンスが高く安全な構成で、多数のアプリケーションと連携できます。