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

前書き

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

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

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

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

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

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

FastCGIプロキシの基本

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

たとえば、FastCGIプロトコルを使用したPHP処理の処理専用のバックエンドに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(「自分自身を繰り返さない」)の原則に従うことです。 これにより、エラーの削減、再利用性の向上、組織の改善が可能になります。 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が継承されます。 継承された値のonlyを使用するか、使用しないかのいずれかです。

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

配列ディレクティブは、他のディレクティブとは異なる方法で子コ​​ンテキストに継承します。 配列ディレクティブからの情報は、子コンテキストonly if they are not present at any place in the child contextに継承されます。 これは、ロケーション内で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;
}

. . .

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

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

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

ただし、上記の値がFsgiWrapのようなものに渡される場合、値は非常に異なって解釈されます。 最初に、スクリプトを実行するために使用する値を決定する初期パスを作成します。 スクリプトを探すためにinitialDOCUMENT_ROOT値を使用します。 ただし、実際のパラメーターをスクリプトに渡すと、PHP-FPMと同様に最終的な値が渡されます。

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

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

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

これは、すべての一般的な構成アイテムを単一のファイルに保持し、必要な構成のどこにでも含めることができることを意味します。 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つのファイルはほぼ同じですが、実際には、1つのパラメーターに複数の値を渡すことについて先ほど説明した問題の結果だけが違います。 fastcgi_paramsファイルにはSCRIPT_FILENAMEパラメータの宣言が含まれていませんが、fastcgi.confファイルには含まれています。

fastcgi_paramsファイルはずっと長い間利用可能でした。 fastcgi_paramsに依存する構成の破損を回避するために、SCRIPT_FILENAMEのデフォルト値を提供することが決定された場合、新しいファイルを作成する必要がありました。 そうしないと、共通ファイルとFastCGIパスの両方の場所でそのパラメーターが設定される可能性があります。 これは、Martin Fjordvald’s excellent post on the history of these two filesで詳細に説明されています。

人気のあるディストリビューションの多くのパッケージメンテナーは、これらのファイルの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モジュールで定義されていますが、ほとんどはコアモジュールのものです。

  • $query_string or $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:この変数には、要求されたファイルのファイルパスが含まれます。 この値は、rootディレクティブとaliasディレクティブの両方、および$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、またはfastcgi_fix_path_infoを使用する場合は最初にキャプチャされたグループです。

  • 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_split_path_infoディレクティブから2番目にキャプチャされたグループを含む$fastcgi_path_info変数に設定される場合があります。 また、一時変数を使用する必要がある場合もあります。これは、その値が他の処理によって上書きされる場合があるためです。

  • PATH_TRANSLATED:このパラメーターは、PATH_INFOに含まれるパス情報を実際のファイルシステムパスにマップします。 通常、これは$document_root$fastcgi_path_infoのようなものに設定されますが、上記のように、後の変数を一時的に保存された変数に置き換える必要がある場合もあります。

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

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

この問題に対処するには、FastCGIプロセッサに正当なリクエストのみを送信するようにしてください。 特定のセットアップのニーズと、FastCGIプロセッサがNginxインスタンスと同じシステム上に存在するかどうかに応じて、さまざまな方法でこれを行うことができます。

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

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

/test.jpg/index.phpのリクエストについて考えてみます。 構成が.phpで終わるすべての要求を、その正当性をテストせずにプロセッサに渡すだけの場合、プロセッサは、仕様に従っている場合、その場所をチェックし、可能であれば実行します。 does notがファイルを見つけると、仕様に従い、/test.jpgファイルの実行を試み、スクリプトの追加パス情報として/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;

        . . .

}

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

たとえば、信頼できないアップロードを許可するディレクトリを具体的に照合し、それらがプロセッサに渡されないようにすることができます。 たとえば、アプリケーションのアップロードディレクトリが/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変数と組み合わせます。

これにより、/test.jpg/index.phpが実行される状況を回避しながら、/index.phpファイルが/users/viewディレクトリに関する情報を処理できるように、/index.php/users/viewのような要求を作成できます。 スクリプトは常に.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は、パフォーマンスが高く安全な構成で、多数のアプリケーションと連携できます。

Related