Wie kann ich verhindern, dass PHP Punktzeichen in GET-, POST- oder COOKIE-Namen ersetzt?

Lesezeit: 10 Minuten

Wenn ich Anforderungsfelder oder Cookies mit einem Punkt/Punkt im Namen übergebe, ersetzt PHP diese automatisch durch Unterstriche. Wenn ich zum Beispiel diesen Code einfüge https://example.com/test.php?x.y=a.b:

<?php
echo $_SERVER['REQUEST_URI'];
echo $_GET['x.y'];
echo $_GET['x_y'];

Die Ausgabe ist:

/test.php?x.y=a.b

a.b

Gibt es beliebig Wie kann ich dieses Verhalten verhindern?

  • .. Warum wandelst du nicht einfach alle Punkte in eine Art Token um, wie zum Beispiel in (~#~) und postest es dann? Wenn Sie die Variablen erhalten, können Sie sie dann wieder zurückkonvertieren. Dies liegt daran, dass wir manchmal Unterstriche einfügen MÜSSEN. Und wir würden sie verlieren, wenn wir alle „_“ in „.“s erneut konvertieren würden …

    – Fernando

    22. September 2011 um 19:20 Uhr

  • Aus der abrufenden Abfrage selbst können Sie den Benutzernamen wie „concat(firstname,’_’,lastname) as user_name verketten.

    – Kaspar Maria

    21. Januar 2013 um 4:49

  • @Kaspar Mary … die Datenbank ist so eingerichtet, dass sie die Spalten „Benutzername“ und „Status“ enthält, und die Benutzernamen werden als „Vorname.Nachname“ gespeichert, sodass ich in SQL kein Concat verwenden kann, da sie bereits mit einem verkettet sind.

    – Rauben

    21. Januar 2013 um 17:00 Uhr

  • @Crisp Danke für den Kommentar! (at) Rob interessantes Problem

    – hek2mgl

    21. Januar 2013 um 5:01

  • Warum gibt es keinen Löschkommentar? 🙂 🙂

    – Chris Prince

    27. Februar 2014 um 2:06


Paige Rutens Benutzeravatar
Paige Ruten

Hier ist die Erklärung von PHP.net, warum das so ist:

Punkte in eingehenden Variablennamen

Normalerweise ändert PHP die Namen von Variablen nicht, wenn sie an ein Skript übergeben werden. Es ist jedoch zu beachten, dass der Punkt (Punkt, Punkt) kein gültiges Zeichen in einem PHP-Variablennamen ist. Schauen Sie sich den Grund dafür an:

<?php
$varname.ext;  /* invalid variable name */
?>

Was der Parser nun sieht, ist eine Variable mit dem Namen $varname, gefolgt vom String-Verkettungsoperator, gefolgt vom Barestring (dh einem String ohne Anführungszeichen, der keinem bekannten Schlüssel oder reservierten Wort entspricht) „ext“. Offensichtlich hat dies nicht das beabsichtigte Ergebnis.

Aus diesem Grund ist es wichtig zu beachten, dass PHP alle Punkte in eingehenden Variablennamen automatisch durch Unterstriche ersetzt.

Das ist von http://ca.php.net/variables.external.

Auch gem dieser Kommentar Diese anderen Zeichen werden in Unterstriche umgewandelt:

Die vollständige Liste der Feldnamenszeichen, die PHP in _ (Unterstrich) umwandelt, ist die folgende (nicht nur ein Punkt):

  • chr(32) ( ) (Leerzeichen)
  • chr(46) (.) (Punkt)
  • chr(91) ([) (open square bracket)
  • chr(128) – chr(159) (various)

So it looks like you’re stuck with it, so you’ll have to convert the underscores back to dots in your script using dawnerd’s suggestion (I’d just use str_replace though.)

  • This is a great explanation of why, but doesn’t answer the original question of “is there any way to get it to stop”; other answers below do provide an answer to the original question.

    – El Yobo

    Aug 10, 2013 at 15:38


  • @ElYobo, @JeremyRuten; good explanation of why? I’m using PHP 5.4 and PHP is still doing this. I’d also love to know why its not deprecated yet. I can only see two reasons for keeping it; register_globals (deprecated since 5.3), and for convenience in ~doing what register globals does manually (in which case the burden should be on the person doing that to map var names how they see fit IMO).

    – spinkus

    Mar 23, 2014 at 2:03

  • Backwards compatibility I assume? Good point, with register globals going the way of the dodo this strange “functionality” could go likewise.

    – El Yobo

    Apr 23, 2014 at 6:49

  • With php7, register globals already rode off into the sunset but the problem is still present.

    – magallanes

    Jan 17, 2016 at 22:55

crb's user avatar
crb

Long-since answered question, but there is actually a better answer (or work-around). PHP lets you at the raw input stream, so you can do something like this:

$query_string = file_get_contents('php://input');

which will give you the $_POST array in query string format, periods as they should be.

You can then parse it if you need (as per POSTer’s comment)

<?php
// Function to fix up PHP's messing up input containing dots, etc.
// `$source` can be either 'POST' or 'GET'
function getRealInput($source) {
    $pairs = explode("&", $source == 'POST' ? file_get_contents("php://input") : $_SERVER['QUERY_STRING']);  $vars = array();  foreach ($pairs as $pair) { $nv = explosion("=", $pair);  $name = urldecode($nv[0]);  $value = urldecode($nv[1]);  $vars[$name] = $value;  } return $vars;  } // Wrapper-Funktionen speziell für GET und POST: function getRealGET() { return getRealInput('GET');  } function getRealPOST() { return getRealInput('POST');  } ?>

Äußerst nützlich für OpenID-Parameter, die sowohl „.“ als auch „.“ enthalten. und „_“, jeweils mit einer bestimmten Bedeutung!

  • Damit dies funktioniert, ersetzen Sie die GET-Parameter file_get_contents("php://input") mit $_SERVER['QUERY_STRING'].

    – Sarel Botha

    27. September 2012 um 22:22

  • Und Sie können das Gleiche auch für Cookies tun $_SERVER['COOKIES']

    – Marcin

    14. Okt. 2012 um 16:09

  • Das ist ein guter Anfang, aber es gibt ein paar Probleme damit. Es verarbeitet keine Array-Werte (z. B. foo.bar).[]=blarg wird nicht als Array, sondern als Skalarvariable namens foo.bar enden[]). Es verursacht außerdem viel Overhead, da alle Werte erneut verarbeitet werden, unabhängig davon, ob sie einen Punkt enthalten oder nicht.

    – El Yobo

    3. August 2013 um 1:45

  • Sehen Sie sich unten meine Lösung an, die die Probleme mit der Implementierung von Rok behebt.

    – El Yobo

    10. August 2013 um 15:37 Uhr

  • Aus irgendeinem Grund $query_string = file_get_contents(‘php://input’); gibt für mich einen leeren String zurück.

    – Chris Prince

    27. Februar 2014 um 2:07

Ich möchte eine tatsächliche Antwort von Johan in einem Kommentar oben hervorheben: Ich habe gerade meinen gesamten Beitrag in ein Array der obersten Ebene eingepackt, das das Problem vollständig umgeht, ohne dass eine umfangreiche Verarbeitung erforderlich ist.

In der Form, die Sie tun

<input name="data[database.username]">  
<input name="data[database.password]">  
<input name="data[something.else.really.deep]">  

anstatt

<input name="database.username"> 
<input name="database.password"> 
<input name="something.else.really.deep">  

und im Post-Handler packen Sie es einfach aus:

$posdata = $_POST['data'];

Für mich war dies eine zweizeilige Änderung, da meine Ansichten vollständig auf Vorlagen beruhten.

Zu Ihrer Information. Ich verwende Punkte in meinen Feldnamen, um Bäume gruppierter Daten zu bearbeiten.

  • In der Tat eine sehr elegante und praktische Lösung, mit dem Nebenvorteil, dass die Formulardaten schön benannt werden.

    – Robinmitra

    13. Mai 2015 um 20:20 Uhr

  • Dies löst das Problem vollständig und hätte als Antwort akzeptiert werden müssen.

    – Brian Klug

    26. Mai 2019 um 17:52 Uhr

Rok Kraljs Benutzeravatar
Rok Kralj

Wollen Sie eine Lösung? normenkonform, und arbeitet mit tiefe Arrays (Zum Beispiel: ?param[2][5]=10) ?

Um alle möglichen Ursachen dieses Problems zu beheben, können Sie ganz oben in Ihrem PHP-Code Folgendes anwenden:

$_GET    = fix( $_SERVER['QUERY_STRING'] );
$_POST   = fix( file_get_contents('php://input') );
$_COOKIE = fix( $_SERVER['HTTP_COOKIE'] );

Die Funktionsweise dieser Funktion ist eine nette Idee, die mir während meiner Sommerferien 2013 gekommen ist. Lassen Sie sich von einer einfachen Regex nicht entmutigen, sie erfasst einfach alle Abfragenamen, codiert sie (damit Punkte erhalten bleiben) und verwendet dann einen Normalen parse_str() Funktion.

function fix($source) {
    $source = preg_replace_callback(
        '/(^|(?<=&))[^=[&]+/',
        function($key) { return bin2hex(urldecode($key[0])); },
        $source
    );

    parse_str($source, $post);
    
    $result = array();
    foreach ($post as $key => $val) {
        $result[hex2bin($key)] = $val;
    }
    return $result;
}

Ja͢cks Benutzeravatar
Jack

Dies liegt daran, dass ein Punkt ein ungültiges Zeichen im Variablennamen ist Grund Das liegt sehr tief in der PHP-Implementierung, daher gibt es (noch) keine einfachen Lösungen.

In der Zwischenzeit können Sie dieses Problem folgendermaßen umgehen:

  1. Zugriff auf die rohen Abfragedaten über beides php://input für POST-Daten oder $_SERVER['QUERY_STRING'] für GET-Daten
  2. Verwendung einer Konvertierungsfunktion.

Die folgende Konvertierungsfunktion (PHP >= 5.4) kodiert die Namen jedes Schlüssel-Wert-Paares in eine hexadezimale Darstellung und führt dann eine reguläre aus parse_str(); Sobald dies erledigt ist, werden die hexadezimalen Namen wieder in ihre ursprüngliche Form zurückversetzt:

function parse_qs($data)
{
    $data = preg_replace_callback('/(?:^|(?<=&))[^=[]+/', function($match) {
        return bin2hex(urldecode($match[0]));
    }, $data);

    parse_str($data, $values);

    return array_combine(array_map('hex2bin', array_keys($values)), $values);
}

// work with the raw query string
$data = parse_qs($_SERVER['QUERY_STRING']);

Oder:

// handle posted data (this only works with application/x-www-form-urlencoded)
$data = parse_qs(file_get_contents('php://input'));

  • Was würde jedoch passieren, wenn dies für etwas anderes verwendet werden müsste, das durchgesendet wurde, und ich tatsächlich das _ in der Variablen benötige?

    – Rauben

    21. Januar 2013 um 5:01

  • @Rob Ich habe die Ausgabe basierend auf Ihrer Frage hinzugefügt; es funktioniert wie erwartet, weil ich die Unterstriche nicht berühre.

    – Jack

    21. Januar 2013 um 5:02

  • Hinweis: Dies ist eine bearbeitete Lösung, die später meinen Code und meine Idee kopiert hat (siehe Änderungsprotokoll). Es sollte von den Moderatoren entfernt werden.

    – Rok Kralj

    22. Juli 2014 um 22:25

  • Anscheinend war es gut genug für dich, das zu nehmen bin2hex() Idee von mir, können wir diese sinnlose Fehde also einfach aufgeben?

    – Jack

    23. Juli 2014 um 0:38

  • @RokKralj Ich hoffe, Sie meinen nicht, dass meine ältere Antwort vollkommen in Ordnung war; Es schien sicher in Ordnung zu sein, bis mir ein großes Versehen auffiel, das in einer Reihe von Änderungen behoben wurde. Die letzte Änderung verwendete denselben regulären Ausdruck, um Namen in einer URL-codierten Zeichenfolge abzugleichen. Mir fällt ehrlich gesagt keine andere Möglichkeit ein, diesen Ausdruck zu schreiben, sonst hätte ich es jetzt getan, um den Anschuldigungen Einhalt zu gebieten.

    – Jack

    23. Juli 2014 um 8:30 Uhr

Dieser Ansatz ist eine veränderte Version von Rok Kraljs, jedoch mit einigen Optimierungen, um die Effizienz zu verbessern (unnötige Rückrufe, Kodierung und Dekodierung nicht betroffener Schlüssel werden vermieden) und Array-Schlüssel korrekt zu verarbeiten.

A Kern mit Tests ist verfügbar und Feedback oder Vorschläge sind hier und da willkommen.

public function fix(&$target, $source, $keep = false) {                        
    if (!$source) {                                                            
        return;                                                                
    }                                                                          
    $keys = array();                                                           

    $source = preg_replace_callback(                                           
        '/                                                                     
        # Match at start of string or &                                        
        (?:^|(?<=&))                                                           
        # Exclude cases where the period is in brackets, e.g. foo[bar.blarg]
        [^=&\[]*                                                               
        # Affected cases: periods and spaces                                   
        (?:\.|%20)                                                             
        # Keep matching until assignment, next variable, end of string or   
        # start of an array                                                    
        [^=&\[]*                                                               
        /x',                                                                   
        function ($key) use (&$keys) {                                         
            $keys[] = $key = base64_encode(urldecode($key[0]));                
            return urlencode($key);                                            
        },                                                                     
    $source                                                                    
    );                                                                         

    if (!$keep) {                                                              
        $target = array();                                                     
    }                                                                          

    parse_str($source, $data);                                                 
    foreach ($data as $key => $val) {                                          
        // Only unprocess encoded keys                                      
        if (!in_array($key, $keys)) {                                          
            $target[$key] = $val;                                              
            continue;                                                          
        }                                                                      

        $key = base64_decode($key);                                            
        $target[$key] = $val;                                                  

        if ($keep) {                                                           
            // Keep a copy in the underscore key version                       
            $key = preg_replace('/(\.| )/', '_', $key);                        
            $target[$key] = $val;                                              
        }                                                                      
    }                                                                          
}                                                                              

  • Was würde jedoch passieren, wenn dies für etwas anderes verwendet werden müsste, das durchgesendet wurde, und ich tatsächlich das _ in der Variablen benötige?

    – Rauben

    21. Januar 2013 um 5:01

  • @Rob Ich habe die Ausgabe basierend auf Ihrer Frage hinzugefügt; es funktioniert wie erwartet, weil ich die Unterstriche nicht berühre.

    – Jack

    21. Januar 2013 um 5:02

  • Hinweis: Dies ist eine bearbeitete Lösung, die später meinen Code und meine Idee kopiert hat (siehe Änderungsprotokoll). Es sollte von den Moderatoren entfernt werden.

    – Rok Kralj

    22. Juli 2014 um 22:25

  • Anscheinend war es gut genug für dich, das zu nehmen bin2hex() Idee von mir, können wir diese sinnlose Fehde also einfach aufgeben?

    – Jack

    23. Juli 2014 um 0:38

  • @RokKralj Ich hoffe, Sie meinen nicht, dass meine ältere Antwort vollkommen in Ordnung war; Es schien sicher in Ordnung zu sein, bis mir ein großes Versehen auffiel, das in einer Reihe von Änderungen behoben wurde. Die letzte Änderung verwendete denselben regulären Ausdruck, um Namen in einer URL-codierten Zeichenfolge abzugleichen. Mir fällt ehrlich gesagt keine andere Möglichkeit ein, diesen Ausdruck zu schreiben, sonst hätte ich es jetzt getan, um den Anschuldigungen Einhalt zu gebieten.

    – Jack

    23. Juli 2014 um 8:30 Uhr

Benutzeravatar von Jeremy Privett
Jeremy Privett

Der Grund dafür ist die alte PHP-Funktionalität „register_globals“. Der . Das Zeichen ist kein gültiges Zeichen in einem Variablennamen, daher wandelt PHP es in einen Unterstrich um, um die Kompatibilität sicherzustellen.

Kurz gesagt: Es ist keine gute Praxis, Punkte in URL-Variablen einzufügen.

  • Es ist auch keine gute Idee, register_globals aktiviert zu haben. Tatsächlich sollte es nach Möglichkeit sofort ausgeschaltet werden.

    – Dawner

    16. September 2008 um 1:57

  • register_globals ist tatsächlich deaktiviert, wie es in PHP5 standardmäßig der Fall ist. > Die . Das Zeichen ist kein gültiges Zeichen in einem Variablennamen. Leider möchte ich es nicht als Variablennamen verwenden (ich behalte es als Schlüssel im $_GET-Wörterbuch), daher bringt diese „Nachdenklichkeit“ in PHP keinen Mehrwert 🙁 Ah, gut…

    Dave Carpeneto

    16. September 2008 um 2:03

  • Es spielt keine Rolle, ob register_globals ein- oder ausgeschaltet ist. PHP führt die Ersetzungen weiterhin durch.

    – Jeremy Privett

    16. September 2008 um 2:09

1454330cookie-checkWie kann ich verhindern, dass PHP Punktzeichen in GET-, POST- oder COOKIE-Namen ersetzt?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy