web-dev-qa-db-fra.com

Lors de la définition de variables d'environnement dans les directives Apache RewriteRule, qu'est-ce qui provoque le préfixe du nom de la variable avec "REDIRECT_"?

J'essaie de définir des variables d'environnement Apache (pour une utilisation en PHP) avec l'indicateur [E=VAR:VAL] sur les règles RewriteRule dans un fichier .htaccess.

J'ai déjà découvert que les variables sont accessibles dans PHP en tant que variables serveur $_SERVER plutôt que $_ENV (ce qui a un certain sens). Cependant, mon problème est que pour certaines règles l'indicateur [E=VAR:VAL] fonctionne comme prévu et que je me retrouve avec une variable $_SERVER['VAR'], mais pour d'autres règles, je termine avec une variable $_SERVER['REDIRECT_VAR'] ou $_SERVER['REDIRECT_REDIRECT_VAR'], etc.

A. Pourquoi une variable d'environnement définie dans Apache à l'aide de l'indicateur [E=VAR:VAL] est-elle renommée en ajoutant le préfixe "REDIRECT_" au nom de la variable?

B. Que puis-je faire pour être sûr d'avoir une variable d'environnement avec un nom inchangé afin que je puisse y accéder dans PHP en tant que $_SERVER['VAR'] sans avoir à rechercher des variantes du nom de la variable ayant une ou plusieurs instances de "REDIRECT_" ajouté à cela?

Solution partielle trouvée . L'ajout de ce qui suit au début des règles de réécriture recrée le fichier ENV: VAR d'origine sur chaque redirection (en y laissant les versions REDIRECT_VAR également):

RewriteCond %{ENV:REDIRECT_VAR} !^$
RewriteRule .* - [E=VAR:%{ENV:REDIRECT_VAR}]
66
trowel

Ce comportement est regrettable et ne semble même pas être documenté.

.htaccess par contexte de répertoire

Voici ce qui semble se passer dans le contexte .htaccess par répertoire (par répertoire):

Supposons qu'Apache traite un fichier .htaccess qui inclut des directives de réécriture.

  1. Apache remplit sa mappe de variables d’environnement avec toutes les variables CGI/Apache standard

  2. La réécriture commence

  3. Les variables d'environnement sont définies dans les directives RewriteRule

  4. Lorsque Apache cesse de traiter les directives RewriteRule (en raison d'un indicateur L ou de la fin du jeu de règles) et que l'URL a été modifiée par une RewriteRule, Apache redémarre le traitement de la demande .

    Si vous ne connaissez pas cette partie, reportez-vous à la documentation de l'indicateur L :

    Ainsi, le jeu de règles peut être exécuté à nouveau depuis le début. Cela se produit le plus souvent si l'une des règles provoque une redirection (interne ou externe), ce qui provoque le redémarrage du processus de demande .
  5. D'après ce que je peux observer, je pense que lorsque # 4 se produit, # 1 est répété, puis les variables d'environnement définies dans les directives RewriteRule sont précédées de REDIRECT_ et ajoutées à la carte vars d'environnement (pas nécessairement dans cet ordre, mais le résultat final consiste en cette combinaison) .

    Cette étape est celle où les noms de variables choisis sont effacés, et dans un instant je vais expliquer pourquoi cela est si important et peu pratique .

Restauration des noms de variables

Lorsque j'ai rencontré ce problème à l'origine, je faisais quelque chose comme ceci dans .htaccess (simplifié):

RewriteCond %{HTTP_Host} (.+)\.projects\.

RewriteRule (.*) subdomains/%1/docroot/$1

RewriteRule (.+/docroot)/ - [L,E=EFFECTIVE_DOCUMENT_ROOT:$1]

Si je définissais la variable d'environnement dans la première RewriteRule, Apache relancerait le processus de réécriture et ajouterait la variable avec REDIRECT_ (étapes 4 et 5 ci-dessus). Par conséquent, je n'y aurais plus accès via le nom que j'ai attribué.

Dans ce cas, la première variable RewriteRule modifie l'URL. Après le traitement des deux variables RewriteRule, Apache redémarre la procédure et traite à nouveau le .htaccess. La deuxième fois, la première RewriteRule est ignorée à cause de la directive RewriteCond, mais la deuxième RewriteRule correspond, définit la variable d'environnement (à nouveau) et, plus important encore, ne modifie pas l'URL. Donc, le processus de demande/réécriture ne recommence pas et le nom de variable que j'ai choisi est celui qui reste. Dans ce cas, j'ai à la fois REDIRECT_EFFECTIVE_DOCUMENT_ROOT et EFFECTIVE_DOCUMENT_ROOT. Si je devais utiliser un indicateur L sur la première RewriteRule, je n’aurais que EFFECTIVE_DOCUMENT_ROOT.

La solution partielle de @ trowel fonctionne de la même manière: les directives de réécriture sont traitées à nouveau, la variable renommée est à nouveau affectée au nom d'origine et, si l'URL ne change pas, le processus est terminé et le nom de variable attribué est bloqué.

Pourquoi ces techniques sont inadéquates

Ces deux techniques souffrent d'un défaut majeur: lorsque les règles de réécriture dans le fichier .htaccess dans lesquelles vous définissez des variables d'environnement réécrivent l'URL dans un répertoire plus imbriqué contenant un fichier .htaccess qui effectue toute réécriture, le nom de variable attribué est effacé .

Disons que vous avez une disposition de répertoire comme celle-ci:

docroot/
        .htaccess
        A.php
        B.php
        sub/
                .htaccess
                A.php
                B.php

Et un docroot/.htaccess comme ceci:

RewriteRule ^A\.php sub/B.php [L]

RewriteRule .* - [E=MAJOR:flaw]

Donc, vous demandez /A.php, et il est réécrit en sub/B.php. Vous avez toujours votre variable MAJOR.

Cependant, si vous avez des directives de réécriture dans docroot/sub/.htaccess (même juste RewriteEngine Off ou RewriteEngine On), votre variable MAJOR disparaît. En effet, une fois que l'URL est réécrite dans sub/B.php, docroot/sub/.htaccess est traitée et si elle contient des directives de réécriture, les directives de réécriture dans docroot/.htaccess ne sont plus traitées. Si vous avez eu un REDIRECT_MAJOR après que docroot/.htaccess ait été traité (par exemple, si vous omettez l'indicateur L de la première RewriteRule), vous l'avez toujours, mais ces directives ne seront pas exécutées à nouveau pour définir le nom de variable que vous avez choisi.

Héritage

Alors, dites que vous voulez:

  1. Définissez les variables d’environnement dans les directives RewriteRule à un niveau particulier de l’arborescence de répertoires (comme docroot/.htaccess)

  2. les ont disponibles dans des scripts à des niveaux plus profonds

  3. les avoir à disposition avec les noms attribués

  4. pouvoir avoir des directives de réécriture dans des fichiers .htaccess plus imbriqués

Une solution possible consiste à utiliser les directives RewriteOptions inherit dans les fichiers .htaccess plus imbriqués. Cela vous permet de réexécuter les directives de réécriture dans des fichiers moins imbriqués et d'utiliser les techniques décrites ci-dessus pour définir les variables avec les noms choisis. Toutefois, notez que cela augmente la complexité, car vous devez être plus prudent dans la rédaction des directives de réécriture dans les fichiers moins imbriqués, de manière à éviter tout problème lorsqu’ils sont réexécutés à partir des répertoires plus profondément imbriqués. Je crois que Apache supprime le préfixe par répertoire du répertoire plus imbriqué et exécute les directives de réécriture dans les fichiers moins imbriqués de cette valeur.@ technique de la truelle.

Autant que je sache, l'utilisation d'une construction telle que %{ENV:REDIRECT_VAR} dans le composant valeur d'un indicateur RewriteRuleE (par exemple [E=VAR:%{ENV:REDIRECT_VAR}]) ne semble pas être documentée :

Cela semble fonctionner, mais si vous voulez éviter de vous fier à quelque chose de non documenté (corrigez-moi si je me trompe), vous pouvez facilement le faire de cette façon:.

RewriteCond %{ENV:REDIRECT_VAR} (.+) RewriteRule .* - [E=VAR:%1]

docroot/.htaccess, avec Apache 2.2.20) fonctionne pour moi:

SetEnvIf REDIRECT_VAR (.+) VAR=$1

Pourquoi?.

Je ne sais pas ce qui justifie de préfixer ces noms avec REDIRECT_ - cela n’est pas surprenant, car cela ne semble pas être mentionné dans les sections de documentation Apache pour les directives mod_rewrite , RewriteRule flags ou Variables d'environnement .

Pour le moment, cela me semble très gênant, en l'absence d'une explication expliquant pourquoi il vaut mieux que de laisser les noms attribués seuls. Le manque de documentation ne fait que renforcer mon scepticisme.

Pouvoir affecter des variables d'environnement dans des règles de réécriture est utile, ou du moins, ce le serait. Mais l'utilité est grandement diminuée par ce comportement de changement de nom. La complexité de cet article illustre à quel point ce comportement est délirant et les obstacles à surmonter pour tenter de le surmonter.

Being able to assign environment variables in rewrite rules is useful, or at least, it would be. But the usefulness is greatly diminished by this name-changing behavior. The complexity of this post illustrates how nuts this behavior and the hoops that have to be jumped through to try to overcome it are.

71
JMM

Je n'ai pas du tout testé cela et je sais que cela ne concerne pas les points A ou B, mais il existe une description de ce problème dans les commentaires de la documentation PHP et des solutions possibles pour accéder à ces variables à l'aide de $_SERVER['VAR']:

http://www.php.net/manual/en/reserved.variables.php#79811

EDIT - d'autres réponses à la question proposée:

A: Les variables d'environnement sont renommées par Apache si elles sont impliquées dans une redirection. Par exemple, si vous avez la règle suivante:

RewriteRule ^index.php - [E=VAR1:'hello',E=VAR2:'world']

Ensuite, vous pouvez accéder à VAR1 et VAR2 en utilisant $_SERVER['VAR1'] et $_SERVER['VAR2']. Cependant, si vous redirigez la page comme suit:

RewriteRule ^index.php index2.php [E=VAR1:'hello',E=VAR2:'world']

Ensuite, vous devez utiliser $_SERVER['REDIRECT_VAR1'], etc.

B: Le meilleur moyen de résoudre ce problème est de traiter les variables pour lesquelles vous souhaitez utiliser PHP. Créez une fonction qui parcourt le tableau $_SERVER et trouve les éléments dont vous avez besoin. Vous pourriez même utiliser une fonction comme celle-ci:

function myGetEnv($key) {
    $prefix = "REDIRECT_";
    if(array_key_exists($key, $_SERVER))
        return $_SERVER[$key];
    foreach($_SERVER as $k=>$v) {
        if(substr($k, 0, strlen($prefix)) == $prefix) {
            if(substr($k, -(strlen($key))) == $key)
                return $v;
        }
    }
    return null;
}
8
thetaiko

Comme je ne souhaite changer aucun de mes codes (je ne peux pas non plus modifier le code des bibliothèques utilisées) , j’ai opté pour l’approche suivante: lors du démarrage de mon application - par exemple. dans mon index.php - Je retravaille le $_ENV superglobal de sorte que les variables précédées du préfixe REDIRECT_ soient réécrites sous leur nom normal prévu:

// Fix ENV vars getting prepended with `REDIRECT_` by Apache
foreach ($_ENV as $key => $value) {
    if (substr($key, 0, 9) === 'REDIRECT_') {
        $_ENV[str_replace('REDIRECT_', '', $key)] = $value;
        putenv(str_replace('REDIRECT_', '', $key) . '=' . $value);
    }
}

Non seulement nous le plaçons directement dans $_ENV, mais nous le stockons également à l'aide de putenv(). De cette façon, le code existant et les bibliothèques - qui pourraient utiliser getenv() - peuvent fonctionner correctement.


Sur une note de bas de page: si vous extrayez des en-têtes - comme HTTP_AUTHORIZATION - dans votre code, vous devez effectuer le même type de manipulation sur $_SERVER:

foreach ($_SERVER as $key => $value) {
    if (substr($key, 0, 9) === 'REDIRECT_') {
        $_SERVER[str_replace('REDIRECT_', '', $key)] = $value;
    }
}
0
Bramus