Erhalten Sie PHP ersetzen zu stoppen ‚‘ Zeichen in $ _GET oder $ _POST Arrays?

StackOverflow https://stackoverflow.com/questions/68651

  •  09-06-2019
  •  | 
  •  

Frage

Wenn ich PHP-Variablen mit . in ihrem Namen über $ _GET PHP übergeben Auto-ersetzt sie durch _ Zeichen. Zum Beispiel:

<?php
echo "url is ".$_SERVER['REQUEST_URI']."<p>";
echo "x.y is ".$_GET['x.y'].".<p>";
echo "x_y is ".$_GET['x_y'].".<p>";

... gibt das folgende:

url is /SpShipTool/php/testGetUrl.php?x.y=a.b
x.y is .
x_y is a.b.

... meine Frage ist: Gibt es jede , wie ich dies nicht erhalten zu stoppen? Kann nicht für das Leben von mir herauszufinden, was ich habe das verdient

PHP-Version Ich laufe mit ist 5.2.4-2ubuntu5.3.

War es hilfreich?

Lösung

Hier ist PHP.net Erklärung dafür, warum sie es tut:

  

Punkte in eingelesenen Variablennamen

     

Normalerweise wird PHP nicht ändern die   Namen von Variablen, wenn sie   in ein Skript übergeben. Wie auch immer, es   sollte, dass der Punkt (zu beachten,   Punkt) ist kein gültiges Zeichen in   ein PHP-Variablennamen. Aus dem Grund,   Blick auf sie:

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

Nun, was   der Parser sieht eine Variable mit dem Namen   $ Varname, gefolgt von der Zeichenfolge   Verkettungsoperator, gefolgt von   die offenen Zeichenkette (String d.h. unquoted   die keine bekannten Schlüssel übereinstimmen oder   reservierte Wörter) 'ext'. Offensichtlich ist dies   nicht das gewünschte Ergebnis hat.

     

Aus diesem Grunde ist es wichtig,   beachten Sie, dass PHP automatisch   ersetzen alle Punkte, die in eingehenden variablen   Namen mit Unterstrichen.

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

Auch nach dieser Kommentar diese anderen Zeichen umgewandelt werden Unterstrichen:

  

Die vollständige Liste der Feldname Zeichen, die PHP konvertiert in _ (Unter) wird die folgende (nicht nur dot):

     
      
  • chr (32) () (Leerzeichen)
  •   
  • CHR (46), (.) (Dot)
  •   
  • chr (91) ([) (offene eckige Klammer)
  •   
  • chr (128) - chr (159) (verschiedene)
  •   

So sieht es aus wie Sie mit ihm stecken, so dass Sie die Unterstreichungen in Ihrem Skript konvertieren Punkte müssen kommen wieder mit Vorschlag der dawnerd (ich würde nur a href = verwenden <" http://php.net / str_replace“rel = "noreferrer"> str_replace though.)

Andere Tipps

Lang da beantworteten Frage, aber es ist eigentlich eine bessere Antwort (oder Workaround). PHP können Sie auf der rel="noreferrer">, so dass man etwas tun kann:

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

, die Ihnen die $ _POST Array in Query-String-Format geben, sollten Perioden, wie sie sein.

Sie können dann analysieren, wenn Sie benötigen (per senden Kommentar )

<?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 = explode("=", $pair);
        $name = urldecode($nv[0]);
        $value = urldecode($nv[1]);
        $vars[$name] = $value;
    }
    return $vars;
}

// Wrapper functions specifically for GET and POST:
function getRealGET() { return getRealInput('GET'); }
function getRealPOST() { return getRealInput('POST'); }
?>

Enorm nützlich für OpenID-Parameter, die beide enthalten ‚‘ und '_', die jeweils mit einer bestimmten Bedeutung!

Hervorhebungen eine tatsächliche Antwort von Johan in einem Kommentar über - wickelte ich gerade meine gesamte Post in einem Top-Level-Array, das das Problem vollständig ohne schwere Verarbeitung umgeht erforderlich

.

In der Form, die Sie tun

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

statt

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

und in den Post-Handler, nur auspacken es:

$posdata = $_POST['data'];

Für mich war dies ein zweizeiliges Wechsel, als meine Ansichten völlig Templat wurden.

Zu Ihrer Information. Ich bin Punkte in meinem Feldnamen mit Bäumen von gruppierten Daten zu bearbeiten.

Die Arbeitsweise dieser Funktion ist ein Genie Hack, den ich mit im Jahr 2013 während meiner Sommerferien kam ich einen Tag einen Blogeintrag darüber schreiben werde.

Dieses Update funktioniert universell und hat tiefe Array-Unterstützung, zum Beispiel a.a[x][b.a]=10. Es nutzt parse_str() hinter den Kulissen mit einem gewissen Vorverarbeitung.

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

Und dann können Sie diese Funktion wie folgt aufrufen, je nach Quelle:

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

Für PHP unter. 5.4: Verwendung base64_encode statt bin2hex und base64_decode statt hex2bin

Dies geschieht, weil eine Periode ein ungültiges Zeichen in Namen einer Variable ist, die Grund für die liegt sehr tief in der Implementierung von PHP, so gibt es keine einfache Korrekturen (noch) nicht.

In der Zwischenzeit können Sie dieses Problem umgehen, durch:

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

der unten angegebenen Umrechnungsfunktion (PHP> = 5,4) kodiert für die Namen der einzelnen Schlüssel-Wert-Paar in eine hexadezimale Darstellung und führt dann eine normale parse_str(); Ist das erledigt, es kehrt die hexadezimalen Namen wieder in ihre ursprüngliche Form:

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'));

Dieser Ansatz ist eine veränderte Version von Rok Kralj ist, aber mit einigen Optimierungen zu arbeiten, die Effizienz zu verbessern (vermeidet unnötige Rückrufe, Codierung und Decodierung auf unbeeinflusst Tasten) und den korrekten Array-Schlüssel zu verarbeiten.

Kern mit Tests zur Verfügung steht und jedes Feedback oder Vorschläge sind willkommen hier oder dort.

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

Der Grund dafür ist alt register_globals Funktionalität wegen PHP geschieht. Das . Charakter ist kein gültiges Zeichen in einem Variablennamen, so PHP coverts es zu einem Unterstrich, um sicherzustellen, dass es ist die Kompatibilität.

Kurz gesagt, es ist keine gute Übungszeiten in URL-Variablen zu tun.

Bei der Suche nach jeder Weg wörtlich erhält PHP zu stoppen zu ersetzen '' Zeichen in $ _GET oder $ _POST Arrays, dann eine solche Art und Weise ist die PHP-Quelle (und in diesem Fall ist es relativ einfach) zu ändern.

ACHTUNG: Ändern von PHP C-Quelle ist eine erweiterte Option

!

Siehe auch diese PHP Bug-Report , die die gleiche Änderung vermuten lässt.

Sie erforschen werden müssen:

  • PHP-C-Quellcode
  • Deaktivieren Sie die . Ersatzprüfung
  • ./ configure machen und implementieren Ihre angepassten Version von PHP

Die Quelle Änderung selbst ist trivial und beinhaltet die Aktualisierung nur die eine Hälfte einer Zeile in main/php_variables.c:

....
/* ensure that we don't have spaces or dots in the variable name (not binary safe) */
for (p = var; *p; p++) {
    if (*p == ' ' /*|| *p == '.'*/) {
        *p='_';
....

Hinweis: Im Vergleich zu dem ursprünglichen || *p == '.' hat kommentiert-out worden


Ausgabe:

ein QUERY_STRING von a.a[]=bb&a.a[]=BB&c%20c=dd gegeben, <?php print_r($_GET); läuft jetzt produziert:

Array
(
    [a.a] => Array
        (
            [0] => bb
            [1] => BB
        )

    [c_c] => dd
)

Weitere Informationen:

  • dieser Patch nur die ursprüngliche Frage Adressen (es stoppt Austausch von Punkten, keine Leerzeichen).
  • auf diesem Patch ausgeführt wird schneller sein als Skript-Level-Lösungen, aber die reinen .php Antworten sind noch allgemein vorzuziehen (weil sie PHP vermeide selbst zu ändern).
  • in der Theorie ein polyfill Ansatz hier möglich ist und Ansätze kombinieren könnte - Test für die C-Pegeländerung mit parse_str() und (falls nicht verfügbar) Fallback zu einem langsameren Methode.

Meine Lösung für dieses Problem war schnell und schmutzig, aber ich mag es immer noch. Ich wollte einfach eine Liste von Dateinamen schreiben, die auf dem Formular überprüft wurden. Ich benutzte base64_encode die Dateinamen im Markup zu kodieren und dann decodiert es nur mit base64_decode vor ihnen.

Nachdem bei Rok-Lösung suchen Ich habe mit einer Version kommen, die die Beschränkungen in meiner Antwort unten richtet, CRB ist oben und auch Rok-Lösung. Sehen Sie eine href="/questions/68651/get-php-to-stop-replacing-characters-in-get-or-post-arrays/18163411#18163411">.


@ CRB Antwort oben ist ein guter Anfang , aber es gibt ein paar Probleme.

  • Es aufarbeitet alles, was übertrieben ist; nur die Felder, die eine haben „“ im Namen müssen wiederaufbereitet werden.
  • Es fehlt Arrays auf die gleiche Art und Weise zu handhaben, dass nativen PHP Verarbeitung der Fall ist, zum Beispiel für Tasten wie "foo.bar []".

Die Lösung unter Adressen beiden dieser Probleme nun (beachten Sie, dass es seit ursprünglich geschrieben wurde aktualisiert). Dies ist etwa 50% schneller als meine Antwort oben in meinen Tests, aber nicht Situationen umgehen, wo die Daten den gleichen Schlüssel (oder eine Taste, die gleich extrahiert wird, zum Beispiel foo.bar und foo_bar als foo_bar beide extrahieren).

<?php

public function fix2(&$target, $source, $keep = false) {                       
    if (!$source) {                                                            
        return;                                                                
    }                                                                          
    preg_match_all(                                                            
        '/                                                                     
        # 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',                                                                   
        $source,                                                               
        $matches                                                               
    );                                                                         

    foreach (current($matches) as $key) {                                      
        $key    = urldecode($key);                                             
        $badKey = preg_replace('/(\.| )/', '_', $key);                         

        if (isset($target[$badKey])) {                                         
            // Duplicate values may have already unset this                    
            $target[$key] = $target[$badKey];                                  

            if (!$keep) {                                                      
                unset($target[$badKey]);                                       
            }                                                                  
        }                                                                      
    }                                                                          
}                                                                              

Nun, die Funktion, die ich unten beinhalten „getRealPostArray ()“, ist keine schöne Lösung, aber es behandelt Arrays und unterstützt beiden Namen: „alpha_beta“ und „alpha.beta“:

  <input type='text' value='First-.' name='alpha.beta[a.b][]' /><br>
  <input type='text' value='Second-.' name='alpha.beta[a.b][]' /><br>
  <input type='text' value='First-_' name='alpha_beta[a.b][]' /><br>
  <input type='text' value='Second-_' name='alpha_beta[a.b][]' /><br>

während var_dump ($ _ POST) erzeugt:

  'alpha_beta' => 
    array (size=1)
      'a.b' => 
        array (size=4)
          0 => string 'First-.' (length=7)
          1 => string 'Second-.' (length=8)
          2 => string 'First-_' (length=7)
          3 => string 'Second-_' (length=8)

var_dump (getRealPostArray ()) erzeugt:

  'alpha.beta' => 
    array (size=1)
      'a.b' => 
        array (size=2)
          0 => string 'First-.' (length=7)
          1 => string 'Second-.' (length=8)
  'alpha_beta' => 
    array (size=1)
      'a.b' => 
        array (size=2)
          0 => string 'First-_' (length=7)
          1 => string 'Second-_' (length=8)

Die Funktion, für was es wert ist:

function getRealPostArray() {
  if ($_SERVER['REQUEST_METHOD'] !== 'POST') {#Nothing to do
      return null;
  }
  $neverANamePart = '~#~'; #Any arbitrary string never expected in a 'name'
  $postdata = file_get_contents("php://input");
  $post = [];
  $rebuiltpairs = [];
  $postraws = explode('&', $postdata);
  foreach ($postraws as $postraw) { #Each is a string like: 'xxxx=yyyy'
    $keyvalpair = explode('=',$postraw);
    if (empty($keyvalpair[1])) {
      $keyvalpair[1] = '';
    }
    $pos = strpos($keyvalpair[0],'%5B');
    if ($pos !== false) {
      $str1 = substr($keyvalpair[0], 0, $pos);
      $str2 = substr($keyvalpair[0], $pos);
      $str1 = str_replace('.',$neverANamePart,$str1);
      $keyvalpair[0] = $str1.$str2;
    } else {
      $keyvalpair[0] = str_replace('.',$neverANamePart,$keyvalpair[0]);
    }
    $rebuiltpair = implode('=',$keyvalpair);
    $rebuiltpairs[]=$rebuiltpair;
  }
  $rebuiltpostdata = implode('&',$rebuiltpairs);
  parse_str($rebuiltpostdata, $post);
  $fixedpost = [];
  foreach ($post as $key => $val) {
    $fixedpost[str_replace($neverANamePart,'.',$key)] = $val;
  }
  return $fixedpost;
}

CRB der Verwendung von I obwohl die $_POST Array als Ganzes neu erstellen wollte im Auge behalten müssen Sie noch sicherstellen, sind Sie Codierung und Decodierung korrekt sowohl auf dem Client und dem Server. Es ist wichtig zu verstehen, wenn ein Charakter ist wirklich ungültig und es ist wirklich gültig . Zusätzlich sind noch Menschen sollten und immer Kundendaten entkommen, bevor es mit jeder Datenbank Befehl ohne Ausnahme mit .

<?php
unset($_POST);
$_POST = array();
$p0 = explode('&',file_get_contents('php://input'));
foreach ($p0 as $key => $value)
{
 $p1 = explode('=',$value);
 $_POST[$p1[0]] = $p1[1];
 //OR...
 //$_POST[urldecode($p1[0])] = urldecode($p1[1]);
}
print_r($_POST);
?>

Ich empfehle dies nur für den Einzelfall nur, offhand ich über die negativen Punkte nicht sicher bin, diese Ihre primären Header-Datei an der Spitze setzen.

Meine aktuelle Lösung (basierend auf i.Vj. Thema Antworten):

function parseQueryString($data)
{
    $data = rawurldecode($data);   
    $pattern = '/(?:^|(?<=&))[^=&\[]*[^=&\[]*/';       
    $data = preg_replace_callback($pattern, function ($match){
        return bin2hex(urldecode($match[0]));
    }, $data);
    parse_str($data, $values);

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

$_GET = parseQueryString($_SERVER['QUERY_STRING']);
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top