it-swarm.com.de

Was bewirkt, wenn Umgebungsvariablen in Apache RewriteRule-Direktiven festgelegt werden, dem Variablennamen "REDIRECT_" vorangestellt wird?

Ich versuche, Apache-Umgebungsvariablen (für die Verwendung in PHP) mit dem Flag [E=VAR:VAL] für RewriteRule-Regeln in einer .htaccess-Datei festzulegen.

Ich habe bereits festgestellt, dass auf die Variablen in PHP als Servervariablen $_SERVER statt auf $_ENV zugegriffen wird (was einen gewissen Sinn ergibt). Mein Problem ist jedoch für einige Regeln, dass das [E=VAR:VAL]-Flag wie erwartet funktioniert, und ich ende mit einer Variablen $_SERVER['VAR']. Bei anderen Regeln ende ich mit einer Variablen $_SERVER['REDIRECT_VAR'] oder $_SERVER['REDIRECT_REDIRECT_VAR'] usw

A. Was bewirkt, dass eine Umgebungsvariable, die in Apache mit dem Flag [E=VAR:VAL] festgelegt ist, umbenannt wird, indem dem Variablennamen "REDIRECT_" vorangestellt wird?

B. Was kann ich tun, um sicherzugehen, dass ich eine Umgebungsvariable mit einem unveränderten Namen erhält, so dass ich in PHP als $_SERVER['VAR'] darauf zugreifen kann, ohne nach Variationen des Variablennamens mit einer oder mehreren Instanzen suchen zu müssen von "REDIRECT_" vorangestellt?

Teillösung gefunden . Wenn Sie am Anfang der Umschreibungsregeln Folgendes hinzufügen, wird die ursprüngliche ENV: VAR bei jeder Umleitung neu erstellt (und die REDIRECT_VAR-Versionen werden dort belassen), falls erforderlich:

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

Dieses Verhalten ist unglücklich und scheint nicht einmal dokumentiert zu sein.

.htaccess pro-dir Kontext

Folgendes scheint in .htaccess pro-Verzeichnis (pro-dir) -Kontext zu geschehen:

Angenommen, Apache verarbeitet eine .htaccess-Datei, die Umschreibungsanweisungen enthält.

  1. Apache füllt seine Umgebungsvariablenzuordnung mit allen Standard-CGI/Apache-Variablen

  2. Das Umschreiben beginnt

  3. Umgebungsvariablen werden in RewriteRule-Direktiven festgelegt

  4. Wenn Apache die Verarbeitung der Anweisungen RewriteRule beendet (aufgrund eines L-Flag oder des Endes des Regelsatzes) und die URL durch eine RewriteRule geändert wurde, startet Apache die Anforderungsverarbeitung neu .

    Wenn Sie mit diesem Teil nicht vertraut sind, lesen Sie die L-Flag-Dokumentation :

    Daher kann der Regelsatz von Anfang an erneut ausgeführt werden. Dies geschieht meistens, wenn eine der Regeln eine interne oder externe Umleitung verursacht, die dazu führt, dass der Anforderungsprozess von vorne beginnt.
  5. Nach allem, was ich beobachten kann, glaube ich, dass wenn # 4 geschieht, # 1 wiederholt wird, dann werden den Umgebungsvariablen, die in RewriteRule-Direktiven gesetzt wurden, REDIRECT_ vorangestellt und zur Umgebungs-Vars-Map hinzugefügt (nicht unbedingt in dieser Reihenfolge). aber das Endergebnis bestehend aus dieser Kombination) .

    In diesem Schritt werden die ausgewählten Variablennamen ausgelöscht, und gleich werde ich erklären, warum das so wichtig und unbequem ist.

Wiederherstellen von Variablennamen

Als ich ursprünglich auf dieses Problem gestoßen bin, habe ich in .htaccess (vereinfacht) so etwas wie das Folgende gemacht:

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

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

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

Wenn ich die Umgebungsvariable in der ersten RewriteRule setzen würde, würde Apache den Umschreibungsprozess erneut starten und die Variable mit REDIRECT_ voranstellen (Schritte # 4 und 5 oben). Daher würde ich über den von mir zugewiesenen Namen den Zugriff darauf verlieren.

In diesem Fall ändert die erste RewriteRule die URL. Nachdem beide RewriteRules verarbeitet wurden, startet Apache die Prozedur neu und verarbeitet den .htaccess erneut. Beim zweiten Mal wird die erste RewriteRule wegen der RewriteCond-Direktive übersprungen, aber die zweite RewriteRule stimmt überein, setzt die Umgebungsvariable (wieder) und ändert die URL nicht. Der Request/Rewriting-Prozess beginnt also nicht noch einmal und der von mir gewählte Variablenname bleibt erhalten. In diesem Fall habe ich tatsächlich sowohl REDIRECT_EFFECTIVE_DOCUMENT_ROOT als auch EFFECTIVE_DOCUMENT_ROOT. Wenn ich ein L-Flag für die erste RewriteRule verwenden würde, hätte ich nur EFFECTIVE_DOCUMENT_ROOT.

Die Teillösung von @trowel funktioniert ähnlich: Die Anweisungen zum Umschreiben werden erneut verarbeitet, die umbenannte Variable wird wieder dem ursprünglichen Namen zugewiesen. Wenn sich die URL nicht ändert, ist der Prozess beendet und der zugewiesene Variablenname bleibt hängen.

Warum sind diese Techniken unzureichend?

Beide Techniken weisen einen großen Fehler auf: Wenn die Umschreiberegeln in der .htaccess-Datei, in der Sie Umgebungsvariablen festlegen, die URL in ein tiefer verschachteltes Verzeichnis mit einer .htaccess-Datei umschreiben, die ein Umschreiben ausführt, wird der zugewiesene Variablenname wieder gelöscht .

Angenommen, Sie haben ein Verzeichnislayout wie folgt:

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

Und ein docroot/.htaccess so:

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

RewriteRule .* - [E=MAJOR:flaw]

Also fordern Sie /A.php an, und es wird in sub/B.php umgeschrieben. Sie haben noch Ihre Variable MAJOR.

Wenn Sie jedoch Anweisungen zum Umschreiben in docroot/sub/.htaccess (auch nur RewriteEngine Off oder RewriteEngine On) haben, wird Ihre Variable MAJOR nicht mehr angezeigt. Wenn nämlich die URL in sub/B.php umgeschrieben wird, wird docroot/sub/.htaccess verarbeitet, und wenn sie Umschreibungsanweisungen enthält, werden Umschreibungsanweisungen in docroot/.htaccess nicht erneut verarbeitet. Wenn Sie nach der Verarbeitung von REDIRECT_MAJOR einen docroot/.htaccess hatten (z. B. wenn Sie das Flag L aus der ersten RewriteRule weglassen), haben Sie es immer noch, aber diese Anweisungen werden nicht erneut ausgeführt, um den ausgewählten Variablennamen festzulegen.

Erbe

Also sagen Sie, Sie möchten:

  1. Umgebungsvariablen in RewriteRule-Direktiven auf einer bestimmten Ebene des Verzeichnisbaums festlegen (z. B. docroot/.htaccess)

  2. haben sie auf tieferen Ebenen in Skripts verfügbar

  3. haben sie mit den zugewiesenen Namen zur Verfügung.

  4. kann Anweisungen in tiefer verschachtelten .htaccess-Dateien umschreiben

Eine mögliche Lösung ist die Verwendung von RewriteOptions inherit-Direktiven in den tiefer geschachtelten .htaccess-Dateien. Auf diese Weise können Sie die Umschreibungsanweisungen in weniger tief verschachtelten Dateien erneut ausführen und die oben beschriebenen Techniken verwenden, um die Variablen mit den ausgewählten Namen festzulegen. Beachten Sie jedoch, dass dies die Komplexität erhöht, da Sie die Umschreibungsanweisungen in den weniger tief geschachtelten Dateien sorgfältiger erstellen müssen, damit sie nicht zu Problemen führen, wenn sie erneut aus den tiefer geschachtelten Verzeichnissen ausgeführt werden. Ich glaube, Apache entfernt das Prä-Dir-Präfix für das tiefer verschachtelte Verzeichnis und führt die Umschreibungsanweisungen in den weniger tief verschachtelten Dateien mit diesem Wert [email protected] Trowels Technik.

Soweit ich sehen kann, scheint die Unterstützung für die Verwendung eines Konstrukts wie %{ENV:REDIRECT_VAR} in der Wertekomponente eines RewriteRuleE-Flags (z. B. [E=VAR:%{ENV:REDIRECT_VAR}]) nicht dokumentiert zu sein.

Es scheint zu funktionieren, aber wenn Sie vermeiden wollen, dass Sie sich auf etwas Undokumentiertes verlassen (bitte korrigieren Sie mich, wenn ich daran falsch liege), können Sie es einfach wie folgt tun:.

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

docroot/.htaccess, mit Apache 2.2.20) funktioniert für mich:

SetEnvIf REDIRECT_VAR (.+) VAR=$1

Warum?.

Ich weiß nicht, was der Grund für das Voranstellen dieser Namen mit REDIRECT_ ist - nicht überraschend, da dies in den Apache-Dokumentationsteilen für mod_rewrite-Direktiven , RewriteRule-Flags oder nicht erwähnt wird Umgebungsvariablen .

Im Moment scheint es mir ein großes Ärgernis zu sein, da es keine Erklärung dafür gibt, warum es besser ist, die zugewiesenen Namen in Ruhe zu lassen. Der Mangel an Dokumentation trägt nur zu meiner Skepsis bei.

Die Möglichkeit, Umgebungsvariablen in Umschreiberegeln zuzuweisen, ist nützlich, oder es wäre zumindest so. Die Nützlichkeit wird jedoch durch dieses Namensänderungsverhalten stark beeinträchtigt. Die Komplexität dieses Beitrags zeigt, wie verrückt dieses Verhalten ist und welche Reifen durchgezogen werden müssen, um es zu überwinden.

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

Ich habe das überhaupt nicht getestet und ich weiß, dass es nicht die Punkte A oder B anspricht, aber es gibt eine Beschreibung dieses Problems in den Kommentaren in der PHP Dokumentation und einige mögliche Lösungen für den Zugriff auf diese Variablen mit $_SERVER['VAR']:

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

EDIT - einige weitere Antworten auf die angebotene Frage:

A: Die Umgebungsvariablen werden von Apache umbenannt, wenn sie an einer Weiterleitung beteiligt sind. Wenn Sie beispielsweise die folgende Regel haben:

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

Dann können Sie mit $_SERVER['VAR1'] und $_SERVER['VAR2'] auf VAR1 und VAR2 zugreifen. Wenn Sie die Seite jedoch so umleiten:

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

Dann müssen Sie $_SERVER['REDIRECT_VAR1'] usw. verwenden.

B: Dieses Problem lässt sich am besten beheben, indem Sie die Variablen verarbeiten, an denen Sie PHP verwenden möchten. Erstellen Sie eine Funktion, die das $_SERVER-Array durchläuft und die Elemente findet, die Sie benötigen. Sie können sogar eine Funktion wie diese verwenden:

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

Da ich meinen Code nicht ändern möchte (und ich kann auch nicht den Code der verwendeten Bibliotheken ändern) , habe ich folgenden Ansatz gewählt: Beim Bootstrapping meiner Anwendung - z. in meinem index.php - Ich überarbeite den $_ENV superglobal so, dass Variablen, denen REDIRECT_ vorangestellt ist, in ihren normalen beabsichtigten Namen geschrieben werden:

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

Wir setzen es nicht nur direkt in $_ENV ein, sondern speichern es auch mit putenv(). Auf diese Weise können vorhandener Code und Bibliotheken - die möglicherweise getenv() verwenden - problemlos funktionieren.


Nebenbemerkung: Wenn Sie in Ihrem Code Header - wie HTTP_AUTHORIZATION - extrahieren, müssen Sie die gleiche Art von Manipulation mit $_SERVER durchführen:

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