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:
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 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.
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
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.
<?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.
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.
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:
Zugriff auf die rohen Abfragedaten über beides php://input für POST-Daten oder $_SERVER['QUERY_STRING'] für GET-Daten
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
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
14543300cookie-checkWie kann ich verhindern, dass PHP Punktzeichen in GET-, POST- oder COOKIE-Namen ersetzt?yes
.. 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