Compréhension et implémentation de la procuration FastCGI dans Nginx

introduction

Nginx est devenue l'une des solutions de serveur Web les plus flexibles et les plus puissantes du marché. Cependant, en termes de conception, il s’agit avant tout d’un serveur proxy. Cette concentration signifie que Nginx est très performant s’agissant de traiter les demandes provenant d’autres serveurs.

Nginx peut utiliser les requêtes proxy via http, FastCGI, uwsgi, SCGI ou memcached. Dans ce guide, nous aborderons le proxy FastCGI, qui est l’un des protocoles de proxy les plus courants.

Pourquoi utiliser FastCGI Proxying?

Le proxy FastCGI dans Nginx est généralement utilisé pour traduire les demandes client pour un serveur d'applications qui ne gère pas ou ne doit pas gérer directement les demandes client. FastCGI est un protocole basé sur l'ancien protocole CGI, ou interface de passerelle commune, destiné à améliorer les performances en n'exécutant pas chaque demande en tant que processus séparé. Il est utilisé pour s’interfacer efficacement avec un serveur qui traite les demandes de contenu dynamique.

L'un des principaux cas d'utilisation du proxy FastCGI dans Nginx concerne le traitement PHP. Contrairement à Apache, qui peut gérer le traitement PHP directement avec l'utilisation du modulemod_php, Nginx doit s'appuyer sur un processeur PHP distinct pour gérer les requêtes PHP. Le plus souvent, ce traitement est géré avecphp-fpm, un processeur PHP qui a été largement testé pour fonctionner avec Nginx.

Nginx avec FastCGI peut être utilisé avec des applications utilisant d'autres langues, à condition qu'un composant accessible soit configuré pour répondre aux demandes FastCGI.

Notions de base sur la procuration de FastCGI

En général, les demandes de proxy utilisent le serveur proxy, dans ce cas-ci Nginx, pour transférer les demandes des clients à un serveur principal. La directive que Nginx utilise pour définir le serveur réel vers lequel utiliser le proxy à l'aide du protocole FastCGI estfastcgi_pass.

Par exemple, pour transmettre toute demande correspondante de PHP à un serveur dédié au traitement du traitement PHP à l'aide du protocole FastCGI, un bloc d'emplacement de base peut ressembler à ceci:

# server context

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

. . .

L’extrait ci-dessus ne fonctionnera pas réellement, car il donne trop peu d’informations. Chaque fois qu'une connexion proxy est établie, la requête d'origine doit être traduite pour garantir que la requête mandatée a un sens pour le serveur principal. Étant donné que nous modifions les protocoles avec une passe FastCGI, cela implique un travail supplémentaire.

Alors que le proxy http à http consiste principalement à augmenter les en-têtes http pour s'assurer que le serveur dispose des informations nécessaires pour répondre au serveur proxy de la part du client, FastCGI est un protocole séparé qui ne peut pas lire les en-têtes http. En raison de cette considération, toute information pertinente doit être transmise au backend par d’autres moyens.

La méthode principale de transmission d'informations supplémentaires lors de l'utilisation du protocole FastCGI consiste à utiliser des paramètres. Le serveur d'arrière-plan doit être configuré pour les lire et les traiter, en modifiant son comportement en fonction de ce qu'il trouve. Nginx peut définir les paramètres FastCGI à l'aide de la directivefastcgi_param.

La configuration minimale qui fonctionnera réellement dans un scénario de proxy FastCGI pour PHP est la suivante:

# 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;
}

. . .

Dans la configuration ci-dessus, nous définissons deux paramètres FastCGI, appelésREQUEST_METHOD etSCRIPT_FILENAME. Ces deux éléments sont nécessaires pour que le serveur principal comprenne la nature de la demande. Le premier lui indique le type d'opération qu'il doit effectuer, tandis que le second indique en amont le fichier à exécuter.

Dans l'exemple, nous avons utilisé des variables Nginx pour définir les valeurs de ces paramètres. La variable$request_method contiendra toujours la méthode http demandée par le client.

Le paramètreSCRIPT_FILENAME est défini sur une combinaison de la variable$document_root et de la variable$fastcgi_script_name. Le$document_root contiendra le chemin vers le répertoire de base, tel que défini par la directiveroot. La variable$fastcgi_script_name sera définie sur l'URI de la demande. Si l'URI de la demande se termine par une barre oblique (/), la valeur de la directivefastcgi_index sera ajoutée à la fin. Ce type de définition d'emplacement auto-référentiel est possible car nous exécutons le processeur FastCGI sur le même ordinateur que notre instance Nginx.

Regardons un autre exemple:

# 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;
}

. . .

Si l'emplacement ci-dessus est sélectionné pour traiter une requête pour/scripts/test/, la valeur deSCRIPT_FILENAME sera une combinaison des valeurs de la directiveroot, de l'URI de la requête et dufastcgi_index directive. Dans cet exemple, le paramètre sera défini sur/var/www/html/scripts/test/index.php.

Nous avons apporté un autre changement significatif à la configuration ci-dessus en spécifiant le moteur FastCGI en utilisant un socket Unix au lieu d'un socket réseau. Nginx peut utiliser l'un ou l'autre type d'interface pour se connecter au FastCGI en amont. Si le processeur FastCGI réside sur le même hôte, un socket Unix est généralement recommandé pour la sécurité.

Rupture de la configuration FastCGI

Une règle clé pour le code maintenable est d’essayer de suivre le principe DRY («Ne vous répétez pas»). Cela aide à réduire les erreurs, augmente la réutilisabilité et permet une meilleure organisation. Étant donné que l'une des recommandations principales pour l'administration de Nginx consiste à toujours définir les directives dans leur champ d'application le plus large, ces objectifs fondamentaux s'appliquent également à la configuration de Nginx.

Lorsqu'il s'agit de configurations de proxy FastCGI, la plupart des instances d'utilisation partagent une grande majorité de la configuration. De ce fait et en raison du fonctionnement du modèle d'héritage Nginx, il est presque toujours avantageux de déclarer des paramètres dans une portée générale.

Déclarer les détails de la configuration FastCGI dans les contextes parents

Une façon de réduire la répétition consiste à déclarer les détails de la configuration dans un contexte parent supérieur. Tous les paramètres en dehors desfastcgi_pass réels peuvent être spécifiés à des niveaux supérieurs. Ils vont cascade vers le bas à l'endroit où la passe se produit. Cela signifie que plusieurs emplacements peuvent utiliser la même configuration.

Par exemple, nous pourrions modifier le dernier extrait de configuration de la section ci-dessus pour le rendre utile dans plusieurs emplacements:

# 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;
}

. . .

Dans l'exemple ci-dessus, les déclarationsfastcgi_param et la directivefastcgi_index sont disponibles dans les deux blocs d'emplacement qui suivent. C'est un moyen de supprimer les déclarations répétitives.

Cependant, la configuration ci-dessus présente un inconvénient sérieux. Si unfastcgi_param est déclaré dans le contexte inférieur,none des valeursfastcgi_param du contexte parent seront hérités. Soit vous utilisezonly les valeurs héritées, soit vous n'en utilisez aucune.

La directivefastcgi_param est une directivearray dans le langage Nginx. Du point de vue des utilisateurs, une directive array est essentiellement toute directive pouvant être utilisée plusieurs fois dans un même contexte. Chaque déclaration suivante ajoutera les nouvelles informations à ce que Nginx sait des déclarations précédentes. La directivefastcgi_param a été conçue comme une directive de tableau afin de permettre aux utilisateurs de définir plusieurs paramètres.

Les directives de tableau héritent des contextes enfants d'une manière différente de certaines autres directives. Les informations des directives de tableau hériteront des contextes enfantsonly if they are not present at any place in the child context. Cela signifie que si vous utilisezfastcgi_param dans votre emplacement, cela effacera complètement les valeurs héritées du contexte parent.

Par exemple, nous pourrions modifier légèrement la configuration ci-dessus:

# 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;
}

. . .

À première vue, vous pouvez penser que les paramètresREQUEST_METHOD etSCRIPT_FILENAME seront hérités dans le deuxième bloc d'emplacement, le paramètreQUERY_STRING étant également disponible pour ce contexte spécifique.

En réalité, lesall des valeurs parentfastcgi_param sont effacés dans le second contexte et seul le paramètreQUERY_STRING est défini. Les paramètresREQUEST_METHOD etSCRIPT_FILENAME resteront inchangés.

Remarque à propos de plusieurs valeurs pour des paramètres dans le même contexte

Une chose qu’il convient de mentionner à ce stade est l’implication de la définition de plusieurs valeurs pour les mêmes paramètres dans un même contexte. Prenons l’exemple suivant comme point de discussion:

# 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;
}

. . .

Dans l'exemple ci-dessus, nous avons défini les paramètresTEST etDOCUMENT_ROOT plusieurs fois dans un même contexte. Puisquefastcgi_param est une directive de tableau, chaque déclaration suivante est ajoutée aux enregistrements internes de Nginx. Le paramètreTEST aura des déclarations dans le tableau le définissant surone,two etthree.

Ce qui est important à réaliser à ce stade, c’est que tout cela sera transmis au back-end de FastCGI sans autre traitement de la part de Nginx. Cela signifie que c'est au processeur FastCGI choisi de décider comment gérer ces valeurs. Malheureusement, différents processeurs FastCGI gèrent les valeurs transmisescompletely differently.

Par exemple, si les paramètres ci-dessus étaient reçus par PHP-FPM, la valeurfinal serait interprétée comme écrasant l'une des valeurs précédentes. Donc, dans ce cas, le paramètreTEST serait défini surthree. De même, le paramètreDOCUMENT_ROOT serait défini suroverride.

Toutefois, si la valeur ci-dessus est transmise à quelque chose comme FsgiWrap, les valeurs sont interprétées très différemment. Tout d'abord, il effectue une passe initiale pour décider des valeurs à utiliser pour exécuter le script. Il utilisera la valeurDOCUMENT_ROOT deinitial pour rechercher le script. Cependant, lorsqu'il transmet les paramètres réels au script, il transmet les valeurs finales, tout comme PHP-FPM.

Cette incohérence et cette imprévisibilité signifient que vous ne pouvez pas et ne devez pas compter sur le serveur pour interpréter correctement vos intentions lorsque vous définissez le même paramètre plusieurs fois. La seule solution sûre consiste à déclarer chaque paramètre une seule fois. Cela signifie également qu'il n'y a rien de tel que de remplacer en toute sécurité une valeur par défaut avec la directivefastcgi_param.

Utilisation de Include to Source Configuration FastCGI à partir d'un fichier séparé

Il existe un autre moyen de séparer vos éléments de configuration communs. Nous pouvons utiliser la directiveinclude pour lire le contenu d'un fichier séparé à l'emplacement de la déclaration de directive.

Cela signifie que nous pouvons conserver tous nos éléments de configuration communs dans un seul fichier et les inclure n'importe où dans notre configuration où nous en avons besoin. Puisque Nginx placera le contenu réel du fichier là où leinclude est appelé, nous n'hériterons pas vers le bas d'un contexte parent vers un enfant. Cela empêchera les valeurs defastcgi_param d'être effacées, nous permettant de définir des paramètres supplémentaires si nécessaire.

Premièrement, nous pouvons définir nos valeurs de configuration FastCGI communes dans un fichier séparé de notre répertoire de configuration. Nous appellerons ce fichierfastcgi_common:

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

Maintenant, nous pouvons lire dans ce fichier où nous souhaitons utiliser ces valeurs de configuration:

# 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;
}

. . .

Ici, nous avons déplacé quelques valeurs communes defastcgi_param vers un fichier appeléfastcgi_common dans notre répertoire de configuration par défaut Nginx. Nous sources ensuite ce fichier lorsque nous voulons insérer les valeurs déclarées dans.

Il y a quelques points à noter à propos de cette configuration.

La première chose à faire est que nous n’avons placé aucune valeur que nous voudrions personnaliser selon l’emplacement dans le fichier que nous prévoyons d’approvisionner. En raison du problème d'interprétation mentionné ci-dessus, qui se produit lors de la définition de plusieurs valeurs pour le même paramètre, et parce que les directives autres que les tableaux ne peuvent être définies qu'une fois par contexte, ne placez que les éléments du fichier commun que vous ne souhaitez pas modifier. Toute directive (ou clé de paramètre) que nous pourrions souhaiter personnaliser en fonction de chaque contexte devrait être exclue du fichier commun.

L’autre chose que vous avez peut-être remarquée est que nous avons défini quelques paramètres FastCGI supplémentaires dans le deuxième bloc d’emplacement. C'est la capacité que nous espérions atteindre. Nous avons pu définir des paramètresfastcgi_param supplémentaires au besoin, sans effacer les valeurs communes.

Utilisation du fichier fastcgi_params ou du fichier fastcgi.conf

Tenant compte de la stratégie ci-dessus, les développeurs Nginx et de nombreuses équipes de conditionnement de la distribution se sont efforcés de fournir un ensemble sain de paramètres communs que vous pouvez inclure dans vos emplacements de passes FastCGI. Ils sont appelésfastcgi_params oufastcgi.conf.

Ces deux fichiers sont en grande partie identiques, la seule différence étant en réalité une conséquence du problème dont nous avons discuté plus tôt, à savoir le fait de transmettre plusieurs valeurs pour un même paramètre. Le fichierfastcgi_params ne contient pas de déclaration pour le paramètreSCRIPT_FILENAME, contrairement au fichierfastcgi.conf.

Le fichierfastcgi_params est disponible depuis beaucoup plus longtemps. Afin d'éviter de briser les configurations qui reposaient surfastcgi_params, lorsque la décision a été prise de fournir une valeur par défaut pourSCRIPT_FILENAME, un nouveau fichier devait être créé. Si vous ne le faites pas, il se peut que ce paramètre ait été défini à la fois dans le fichier commun et dans l'emplacement de passe FastCGI. Ceci est décrit en détail dansMartin Fjordvald’s excellent post on the history of these two files.

De nombreux responsables de paquet pour les distributions populaires ont choisi d'inclure un seul de ces fichiers ou de refléter exactement leur contenu. Si vous n'en avez qu'un, utilisez celui que vous avez. N'hésitez pas à le modifier pour l'adapter également à vos besoins.

Si vous disposez de ces deux fichiers, pour la plupart des emplacements de passes FastCGI, il est probablement préférable d'inclure le fichierfastcgi.conf, car il inclut une déclaration pour le paramètreSCRIPT_FILENAME. Cela est généralement souhaitable, mais vous souhaiterez peut-être personnaliser cette valeur dans certains cas.

Ceux-ci peuvent être inclus en référençant leur emplacement par rapport au répertoire de configuration racine de Nginx. Le répertoire de configuration racine de Nginx est généralement quelque chose comme/etc/nginx lorsque Nginx a été installé avec un gestionnaire de paquets.

Vous pouvez inclure les fichiers comme ceci:

# server context

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

    . . .

}

Ou comme ceci:

# server context

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

    . . .

}

Directives, paramètres et variables FastCGI importants

Dans les sections ci-dessus, nous avons défini un bon nombre de paramètres, souvent des variables Nginx, comme moyen de démonstration d’autres concepts. Nous avons également introduit des directives FastCGI sans trop d’explication. Dans cette section, nous aborderons certaines des directives courantes à définir, les paramètres que vous devrez peut-être modifier et certaines variables pouvant contenir les informations dont vous avez besoin.

Directives FastCGI communes

Les éléments suivants représentent certaines des directives les plus utiles pour utiliser les passes FastCGI:

  • fastcgi_pass: directive réelle qui transmet les requêtes du contexte actuel au backend. Ceci définit l'emplacement où le processeur FastCGI peut être atteint.

  • fastcgi_param: directive de tableau qui peut être utilisée pour définir des paramètres sur des valeurs. Le plus souvent, cela est utilisé en conjonction avec des variables Nginx pour définir les paramètres FastCGI sur des valeurs spécifiques à la demande.

  • try_files: pas une directive spécifique à FastCGI, mais une directive courante utilisée dans les emplacements de passes FastCGI. Cela est souvent utilisé dans le cadre d'une routine de désinfection de demande pour s'assurer que le fichier demandé existe avant de le transmettre au processeur FastCGI.

  • include: Encore une fois, pas une directive spécifique à FastCGI, mais une directive qui est fortement utilisée dans les contextes de passage FastCGI. Le plus souvent, cela est utilisé pour inclure des détails de configuration communs communs dans plusieurs emplacements.

  • fastcgi_split_path_info: cette directive définit une expression régulière avec deux groupes capturés. Le premier groupe capturé est utilisé comme valeur pour la variable$fastcgi_script_name. Le deuxième groupe capturé est utilisé comme valeur pour la variable$fastcgi_path_info. Ces deux méthodes sont souvent utilisées pour analyser correctement la requête afin que le processeur sache quelles parties de la requête sont les fichiers à exécuter et quelles parties sont des informations supplémentaires à transmettre au script.

  • fastcgi_index: Ceci définit le fichier d'index qui doit être ajouté aux valeurs$fastcgi_script_name qui se terminent par une barre oblique (/). Ceci est souvent utile si le paramètreSCRIPT_FILENAME est défini sur$document_root$fastcgi_script_name et que le bloc d'emplacement est configuré pour accepter les demandes avec des informations après le fichier.

  • fastcgi_intercept_errors: Cette directive définit si les erreurs reçues du serveur FastCGI doivent être gérées par Nginx ou transmises directement au client.

Les directives ci-dessus représentent l'essentiel de ce que vous utiliserez lors de la conception d'un laissez-passer FastCGI typique. Vous ne pouvez pas utiliser tout le temps tout le temps, mais nous pouvons commencer à voir qu'ils interagissent assez étroitement avec les paramètres FastCGI et les variables dont nous parlerons ensuite.

Variables communes utilisées avec FastCGI

Avant que nous puissions parler des paramètres que vous êtes susceptible d’utiliser avec les passes FastCGI, nous devrions parler un peu de certaines variables Nginx courantes dont nous tirerons parti en définissant ces paramètres. Certaines d’entre elles sont définies par le module FastCGI de Nginx, mais la plupart proviennent du module Core.

  • $query_string or $args: les arguments donnés dans la demande client d'origine.

  • $is_args: sera égal à "?" s'il y a des arguments dans la demande et sera définie sur une chaîne vide sinon. Ceci est utile lors de la construction de paramètres avec ou sans arguments.

  • $request_method: Ceci indique la méthode de demande client d'origine. Cela peut être utile pour déterminer si une opération doit être autorisée dans le contexte actuel.

  • $content_type: Ceci est défini sur l'en-tête de demandeContent-Type. Le proxy a besoin de ces informations si la requête de l’utilisateur est un POST afin de traiter correctement le contenu qui suit.

  • $content_length: Ceci est défini sur la valeur de l'en-têteContent-Length du client. Cette information est requise pour toutes les demandes POST client.

  • $fastcgi_script_name: Celui-ci contiendra le fichier de script à exécuter. Si la requête se termine par une barre oblique (/), la valeur de la directivefastcgi_index sera ajoutée à la fin. Dans le cas où la directivefastcgi_split_path_info est utilisée, cette variable sera définie sur le premier groupe capturé défini par cette directive. La valeur de cette variable doit indiquer le script à exécuter.

  • $request_filename: Cette variable contiendra le chemin d'accès au fichier demandé. Il obtient cette valeur en prenant la valeur de la racine du document courant, en tenant compte à la fois des directivesroot etalias, et de la valeur de$fastcgi_script_name. C'est une manière très flexible d'assigner le paramètreSCRIPT_FILENAME.

  • $request_uri: La demande entière telle que reçue du client. Cela inclut le script, toute information de chemin supplémentaire, ainsi que toute chaîne de requête.

  • $fastcgi_path_info: cette variable contient des informations de chemin supplémentaires qui peuvent être disponibles après le nom du script dans la requête. Cette valeur contient parfois un autre emplacement que le script à exécuter doit connaître. Cette variable obtient sa valeur du deuxième groupe de regex capturé lors de l'utilisation de la directivefastcgi_split_path_info.

  • $document_root: cette variable contient la valeur racine actuelle du document. Celui-ci sera défini selon les directivesroot oualias.

  • $uri: cette variable contient l'URI actuel avec la normalisation appliquée. Certaines directives réécrivant ou redirigeant en interne pouvant avoir un impact sur l'URI, cette variable exprimera ces modifications.

Comme vous pouvez le constater, de nombreuses variables s’offrent à vous lorsque vous décidez comment définir les paramètres FastCGI. Beaucoup d'entre eux sont similaires, mais présentent des différences subtiles qui auront une incidence sur l'exécution de vos scripts.

Paramètres FastCGI communs

Les paramètres FastCGI représentent les informations sur la valeur clé que nous souhaitons mettre à la disposition du processeur FastCGI auquel nous envoyons la demande. Toutes les applications n’ayant pas besoin des mêmes paramètres, vous devrez souvent consulter la documentation de l’application.

Certains de ces paramètres sont nécessaires pour que le processeur identifie correctement le script à exécuter. D'autres sont mis à la disposition du script, en modifiant éventuellement son comportement s'il est configuré pour utiliser les paramètres définis.

  • QUERY_STRING: ce paramètre doit être défini sur n'importe quelle chaîne de requête fournie par le client. Ce sont généralement des paires clé-valeur fournies après un "?" Dans l'URI. En règle générale, ce paramètre est défini sur les variables$query_string ou$args, qui doivent toutes deux contenir les mêmes données.

  • REQUEST_METHOD: ce paramètre indique au processeur FastCGI quel type d'action a été demandé par le client. C'est l'un des rares paramètres à définir pour que la passe fonctionne correctement.

  • CONTENT_TYPE: Si la méthode de requête définie ci-dessus est «POST», ce paramètre doit être défini. Il indique le type de contenu attendu par le processeur FastCGI. Ceci est presque toujours défini uniquement sur la variable$content_type, qui est définie en fonction des informations de la requête d'origine.

  • CONTENT_LENGTH: Si la méthode de requête est «POST», ce paramètre doit être défini. Cela indique la longueur du contenu. Ceci est presque toujours défini sur$content_length, une variable qui tire sa valeur des informations de la demande client d'origine.

  • SCRIPT_NAME: ce paramètre est utilisé pour indiquer le nom du script principal qui sera exécuté. Il s'agit d'un paramètre extrêmement important qui peut être défini de différentes manières en fonction de vos besoins. Souvent, il est défini sur$fastcgi_script_name, qui devrait être l'URI de la demande, l'URI de la demande avec lefastcgi_index ajouté s'il se termine par une barre oblique, ou le premier groupe capturé si vous utilisezfastcgi_fix_path_info.

  • SCRIPT_FILENAME: ce paramètre spécifie l'emplacement réel sur le disque du script à exécuter. En raison de sa relation avec le paramètreSCRIPT_NAME, certains guides suggèrent d'utiliser$document_root$fastcgi_script_name. Une autre alternative qui présente de nombreux avantages est d'utiliser$request_filename.

  • REQUEST_URI: il doit contenir l'URI de demande complet et non modifié, avec le script à exécuter, des informations de chemin supplémentaires et tous les arguments. Certaines applications préfèrent analyser cette information elles-mêmes. Ce paramètre leur donne les informations nécessaires pour le faire.

  • PATH_INFO: Sicgi.fix_pathinfo est défini sur «1» dans le fichier de configuration PHP, cela contiendra toutes les informations de chemin supplémentaires ajoutées après le nom du script. Ceci est souvent utilisé pour définir un argument de fichier sur lequel le script doit agir. Définircgi.fix_pathinfo sur «1» peut avoir des implications sur la sécurité si les requêtes de script ne sont pas nettoyées par d'autres moyens (nous en discuterons plus tard). Parfois, il est défini sur la variable$fastcgi_path_info, qui contient le deuxième groupe capturé à partir de la directivefastcgi_split_path_info. D'autres fois, une variable temporaire devra être utilisée car cette valeur est parfois altérée par d'autres traitements.

  • PATH_TRANSLATED: ce paramètre mappe les informations de chemin contenues dansPATH_INFO dans un chemin de système de fichiers réel. Habituellement, cela sera défini sur quelque chose comme$document_root$fastcgi_path_info, mais parfois la dernière variable doit être remplacée par la variable enregistrée temporaire comme indiqué ci-dessus.

Vérification des demandes avant de passer à FastCGI

Un sujet essentiel que nous n'avons pas encore abordé concerne la manière de transmettre en toute sécurité des requêtes dynamiques à votre serveur d'applications. Transmettre toutes les demandes à l'application principale, quelle que soit leur validité, est non seulement inefficace, mais également dangereux. Il est possible pour des attaquants de créer des requêtes malveillantes pour que votre serveur exécute du code arbitraire.

Afin de résoudre ce problème, nous devons nous assurer que nous n'envoyons que des demandes légitimes à nos processeurs FastCGI. Nous pouvons le faire de différentes manières en fonction des besoins de notre configuration particulière et du fait que le processeur FastCGI est installé sur le même système que notre instance Nginx.

Une règle de base qui devrait informer la manière dont nous concevons notre configuration est que nous ne devrions jamais permettre aucun traitement et interprétation des fichiers utilisateur. Il est relativement facile pour des utilisateurs malveillants d’incorporer un code valide dans des fichiers apparemment innocents, tels que des images. Une fois qu'un fichier comme celui-ci est chargé sur notre serveur, nous devons nous assurer qu'il ne parviendra jamais à notre processeur FastCGI.

Le problème majeur que nous essayons de résoudre ici est celui qui est spécifié dans la spécification CGI. La spécification vous permet de spécifier un fichier de script à exécuter, suivi d'informations de chemin supplémentaires pouvant être utilisées par le script. Ce modèle d'exécution permet aux utilisateurs de demander un URI pouvant ressembler à un script légitime, tandis que la partie réelle qui sera exécutée se situera plus tôt dans le chemin.

Prenons une demande pour/test.jpg/index.php. Si votre configuration transmet simplement chaque demande se terminant par.php à votre processeur sans tester sa légitimité, le processeur, s'il suit les spécifications, vérifiera cet emplacement et l'exécutera si possible. Sidoes not trouve le fichier, il suivra la spécification et tentera d'exécuter le fichier/test.jpg, marquant/index.php comme informations de chemin supplémentaires pour le script. Comme vous pouvez le constater, cela pourrait avoir des conséquences très indésirables en combinaison avec l’idée de téléversement par les utilisateurs.

Il existe différentes manières de résoudre ce problème. Le plus simple, si votre application ne s'appuie pas sur cette information de chemin supplémentaire pour le traitement, consiste simplement à la désactiver dans votre processeur. Pour PHP-FPM, vous pouvez désactiver cette option dans votre fichierphp.ini. Par exemple, sur les systèmes Ubuntu, vous pouvez éditer ce fichier:

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

Recherchez simplement l'optioncgi.fix_pathinfo, décommentez-la et réglez-la sur «0» pour désactiver cette «fonctionnalité»:

cgi.fix_pathinfo=0

Redémarrez votre processus PHP-FPM pour effectuer le changement:

sudo service php5-fpm restart

Cela fera que PHP ne tentera jamais que l'exécution sur le dernier composant d'un chemin. Donc, dans notre exemple ci-dessus, si le fichier/test.jpg/index.php n'existait pas, PHP ferait correctement une erreur au lieu d'essayer d'exécuter/test.jpg.

Une autre option, si notre processeur FastCGI est sur le même ordinateur que notre instance Nginx, consiste simplement à vérifier l’existence des fichiers sur le disque avant de les transmettre au processeur. Si le fichier/test.jgp/index.php n’existe pas, erreur. Si tel est le cas, envoyez-le au serveur pour traitement. En pratique, cela se traduira en grande partie par le même comportement que ci-dessus:

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

        . . .

}

Si votre applicationdoes s'appuie sur le comportement des informations de chemin pour une interprétation correcte, vous pouvez toujours autoriser ce comportement en toute sécurité en effectuant des vérifications avant de décider d'envoyer la demande au backend.

Par exemple, nous pourrions faire spécifiquement correspondre les répertoires dans lesquels nous autorisons les téléchargements non fiables et nous assurer qu'ils ne sont pas transmis à notre processeur. Par exemple, si le répertoire de téléchargement de notre application est/uploads/, nous pourrions créer un bloc d'emplacement comme celui-ci qui correspond avant l'évaluation des expressions régulières:

location ^~ /uploads {
}

A l'intérieur, nous pouvons désactiver tout type de traitement pour les fichiers PHP:

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

L'emplacement parent correspondra à toute requête commençant par/uploads et toute requête traitant des fichiers PHP renverra une erreur 403 au lieu de l'envoyer à un backend.

Vous pouvez également utiliser la directivefastcgi_split_path_info pour définir manuellement la partie de la demande qui doit être interprétée comme le script et la partie qui doit être définie comme les informations de chemin supplémentaires à l'aide d'expressions régulières. Cela vous permet de continuer à utiliser la fonctionnalité d’information de chemin, mais de définir exactement ce que vous considérez comme le script et ce que vous considérez comme le chemin.

Par exemple, nous pouvons configurer un bloc d'emplacement qui considère la première instance d'un composant de chemin se terminant par.php comme le script à exécuter. Le reste sera considéré comme l'info de chemin supplémentaire. Cela signifie que dans l'instance d'une requête pour/test.jpg/index.php, le chemin complet peut être envoyé au processeur en tant que nom de script sans info de chemin supplémentaire.

Cet emplacement peut ressembler à quelque chose comme ceci:

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;
}

Le bloc ci-dessus devrait fonctionner pour les configurations PHP oùcgi.fix_pathinfo est défini sur «1» pour permettre des informations de chemin supplémentaires. Ici, notre bloc d'emplacement correspond non seulement aux demandes qui se terminent par.php, mais aussi à celles avec.php juste avant une barre oblique (/) indiquant qu'un composant de répertoire supplémentaire suit.

À l'intérieur du bloc, la directivefastcgi_split_path_info définit deux groupes capturés avec des expressions régulières. Le premier groupe correspond à la partie de l'URI du début à la première instance de.php et la place dans la variable$fastcgi_script_name. Il place ensuite toutes les informations à partir de ce point dans un deuxième groupe capturé, qu'il stocke dans une variable appelée$fastcgi_path_info.

Nous utilisons la directiveset pour stocker la valeur contenue dans$fastcgi_path_info à ce stade dans une variable appelée$orig_path. C'est parce que la variable$fastcgi_path_info sera effacée dans un instant par notre directivetry_files.

Nous testons le nom du script que nous avons capturé ci-dessus en utilisanttry_files. Il s’agit d’une opération de fichier qui garantit que le script que nous essayons d’exécuter est sur le disque. Cependant, cela a également pour effet secondaire d'effacer la variable$fastcgi_path_info.

Après avoir effectué la passe FastCGI conventionnelle, nous définissons lesSCRIPT_FILENAME comme d'habitude. Nous définissons également lesPATH_INFO sur la valeur que nous avons déchargée dans la variable$orig_path. Bien que notre$fastcgi_path_info ait été effacé, sa valeur d'origine est conservée dans cette variable. Nous avons également défini le paramètrePATH_TRANSLATED pour mapper les informations de chemin supplémentaires à l'emplacement où elles existent sur le disque. Nous faisons cela en combinant la variable$document_root avec la variable$orig_path.

Cela nous permet de construire des requêtes comme/index.php/users/view afin que notre fichier/index.php puisse traiter les informations sur le répertoire/users/view, tout en évitant les situations où/test.jpg/index.php sera exécuté. Il définira toujours le script sur le composant le plus court se terminant par.php, évitant ainsi ce problème.

Nous pourrions même utiliser cette directive avec un alias si nous devons modifier l'emplacement de nos fichiers de script. Nous aurions juste à en tenir compte à la fois dans notre en-tête d'emplacement et dans la définition defastcgi_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;
}

Ceux-ci vous permettront d'exécuter vos applications qui utilisent le paramètrePATH_INFO en toute sécurité. N'oubliez pas que vous devrez changer l'optioncgi.fix_pathinfo de votre fichierphp.ini sur "1" pour que cela fonctionne correctement. Vous devrez peut-être également désactiver lessecurity.limit_extensions dans votre fichierphp-fpm.conf.

Conclusion

J'espère que vous avez maintenant une meilleure compréhension des capacités de proxy de FastCGI de Nginx. Cette capacité permet à Nginx d’exercer ses atouts en matière de traitement rapide des connexions et de service de contenu statique, tout en déchargeant les responsabilités relatives au contenu dynamique des logiciels mieux adaptés. FastCGI permet à Nginx de fonctionner avec un grand nombre d'applications, dans des configurations performantes et sécurisées.

Related