So prüfen Sie, ob eine unvollständige POST-Anfrage in PHP vorliegt

8

Ich stehe vor einem Problem, wenn ein entfernter Webclient mit langsamer Verbindung keine vollständige POST-Anfrage mit multipart/form-data Inhalt senden kann, aber PHP verwendet noch teilweise empfangene Daten, um $_POST -Array aufzufüllen. Daher kann ein Wert in $_POST -Array unvollständig sein und mehr Werte können fehlen. Ich habe versucht, dieselbe Frage in Apache-Liste zuerst und bekam eine Antwort , dass Apache den Anfragetext nicht zwischenspeichert und ihn als PHP an das PHP-Modul weiterleitet riesiger Klecks.

Hier ist meine Beispiel-POST-Anfrage:

%Vor%

Sie können sehen, dass Content-Length ist 10000 Bytes, aber ich sende nur eine var a=A .

Das PHP-Skript ist:

%Vor%

Der Webserver wartet ungefähr 10 Sekunden auf den Rest meiner Anfrage (aber ich sende nichts) und gibt dann diese Antwort zurück:

%Vor%

Hier ist meine Frage: Wie kann ich in PHP verifizieren, dass die Postanfrage vollständig empfangen wurde? $_SERVER['CONTENT_LENGTH'] würde 10000 aus dem Anfrage-Header anzeigen, aber gibt es eine Möglichkeit, die tatsächliche Länge des Inhalts zu überprüfen?

    
spatar 27.11.2013, 23:57
quelle

13 Antworten

3
  

Ich vermute, dass der Remote-Client eigentlich ein Browser mit HTML-Seite ist.   Ansonsten lass es mich wissen und ich werde versuchen, meine Lösung anzupassen.

Sie können das Feld <input type="hidden" name="complete"> (zum Beispiel) als den letzten Parameter hinzufügen. in PHP überprüfen Sie zuerst, ob dieser Parameter vom Client gesendet wurde. Wenn dieser Parameter gesendet wird, können Sie sicher sein, dass Sie alle Daten erhalten haben.

Nun bin ich nicht sicher, ob die Reihenfolge der Parameter gemäß RFC (sowohl HTML als auch HTTP) erhalten bleiben muss. aber ich habe einige Variationen ausprobiert und ich habe gesehen, dass die Reihenfolge in der Tat eingehalten wurde.

Bessere Lösung wird sein, berechnen (auf der Client-Seite) Hash der Parameter und senden Sie ihn als einen anderen Parameter. So können Sie absolut sicher sein, dass Sie die gesamten Daten erhalten haben. Aber das klingt kompliziert ...

    
MeNa 04.12.2013, 09:41
quelle
1

Soweit ich weiß, gibt es keine Möglichkeit zu überprüfen, ob die Größe des empfangenen Inhalts mit dem Wert des Headers Content-Length übereinstimmt, wenn Sie multipart/form-data als Content-Type verwenden, weil Sie den rohen Inhalt nicht erhalten können.

1) Wenn Sie Content-Type (z. B. application/x-www-form-urlencoded ) ändern können, können Sie php://input lesen, das den unformatierten Inhalt der Anfrage enthält. Die Größe von php://input sollte mit Content-Length übereinstimmen (vorausgesetzt, der Wert von Content-Length ist korrekt). Wenn es eine Übereinstimmung gibt, können Sie $_POST verwenden, um den verarbeiteten Inhalt (reguläre Postdaten) zu erhalten. Lies über php://input hier .

2) Oder Sie können die Daten auf dem Client serialisieren und als text/plain senden. Der Server kann die Größe wie oben beschrieben überprüfen. Der Server muss den empfangenen Inhalt deserialisieren, um damit arbeiten zu können. Und wenn der Client einen Hash der serialisierten Daten generiert und diese in einem Header (zB X-Content-Hash ) mitschickt, kann der Server auch einen Hash erzeugen und prüfen, ob er mit dem im Header übereinstimmt. Sie müssen den Hash nicht überprüfen und können zu 100% sicher sein, dass der Inhalt korrekt ist.

3) Wenn Sie Content-Type nicht ändern können, benötigen Sie etwas anderes als die Größe, um den Inhalt zu überprüfen. Der Client könnte einen zusätzlichen Header (etwas wie X-Form-Data-Fields ) verwenden, um die Felder / Schlüssel / Namen des von Ihnen gesendeten Inhalts zu summieren. Der Server könnte dann prüfen, ob alle in der Kopfzeile genannten Felder im Inhalt vorhanden sind.

4) Eine andere Lösung wäre, dass der Client einen vordefinierten Schlüssel / Wert als letzten Eintrag im Inhalt hat. Etwas wie:

%Vor%

Der Server kann überprüfen, ob dieses Feld im Inhalt vorhanden ist, wenn dies der Fall ist, muss der Inhalt vollständig sein.

update

Wenn Sie binäre Daten übergeben müssen, können Sie Option 1 nicht verwenden, können aber weiterhin Option 2 verwenden:

Der Client kann base64 die binären Einträge codieren, die Daten serialisieren (mit jeder beliebigen Technik), einen Hash der serialisierten Daten generieren, den Hash als Header und die Daten als Body senden. Der Server kann einen Hash des empfangenen Inhalts generieren, den Hash mit dem im Header überprüfen (und eine Nichtübereinstimmung melden), den Inhalt deserialisieren, base64 die Binäreinträge dekodieren.

Das ist ein bisschen mehr Arbeit, als einfach multipart/form-data zu verwenden, aber der Server kann mit einer 100% igen Garantie verifizieren, dass der Inhalt der gleiche ist wie der, den der Client gesendet hat.

    
Jasper N. Brouwer 07.12.2013 20:32
quelle
1

Wenn Sie den Enktyp in

ändern können %Vor%

das können Sie überprüfen

%Vor%

gegen

%Vor%     
corretge 10.12.2013 10:58
quelle
0

Sie werden wahrscheinlich durch Limits in Apache oder PHP abgeschnitten. Ich glaube, Apache hat auch eine Konfigurationsvariable dafür.

Hier sind die PHP-Einstellungen;

php.ini

%Vor%

.htaccess

%Vor%     
vokx 28.11.2013 00:05
quelle
0

In Bezug auf Formularwerte, die aufgrund von Konnektivitätsproblemen vollständig fehlen, können Sie einfach überprüfen, ob sie festgelegt sind:

%Vor%

Für die großen Formulardaten (z. B. ein Bildupload) können Sie die Größe der empfangenen Datei mit

überprüfen %Vor%

Eine einfache Lösung verwendet möglicherweise JavaScript, um die Dateigröße auf der Clientseite zu berechnen und diesen Wert als verdeckte Eingabe beim Senden des Formulars an das Formular anzufügen. Sie erhalten die Dateigröße in JS mit etwas wie

%Vor%

Referenz: Validierung der Größe von JavaScript-Dateiuploads

Wenn die Eingabe des versteckten Formulars beim Hochladen der Datei der Größe der hochgeladenen Datei entspricht, wurde die Anfrage nicht durch Probleme mit der Netzwerkverbindung unterbrochen.

    
nightowl 04.12.2013 17:25
quelle
0

vielleicht können Sie mit einer gültigen Variable, aber nicht mit der Länge überprüfen, zB:

%Vor%     
andy.why 05.12.2013 14:58
quelle
0

Ich würde auch empfehlen, einen hidden -Wert zu verwenden, oder Hashing wie MeNa erwähnt. (Das Problem besteht darin, dass einige Algorithmen anders als Plattformen implementiert sind, so dass sich Ihr CRC32 in js von einem CRC32 in PHP unterscheidet. Aber mit einigen Tests sollten Sie in der Lage sein, einen kompatiblen zu finden)

Ich werde vorschlagen, symmetrische Verschlüsselung zu verwenden, nur für die Tatsache, dass es eine Option ist. (Ich glaube nicht, dass es schneller ist als Hashing). Verschlüsselung bietet neben Vertraulichkeit auch Integrität, d. ist diese empfangene Nachricht diejenige, die gesendet wurde.

Obwohl Streamcipher sehr schnell sind, können Blockcipher wie AES auch sehr schnell sein, aber das hängt von Ihrem System, den verwendeten Sprachen usw. ab (auch hier bedeuten verschiedene Implementierungen, dass nicht alle Verschlüsselungen gleich sind)

Wenn Sie die Nachricht nicht entschlüsseln können (oder es zu einem verzerrten Chaos kommt), war die Nachricht unvollständig.

Aber ernsthaft , verwenden Sie Hashing. Hasse den POST auf dem Client, überprüfe zuerst die Länge des Hashs auf dem Server. (Einige?) Hashes sind feste Länge, wenn also die Länge nicht übereinstimmt, ist es falsch. Hash-Hash für den empfangenen POST und Vergleich mit dem POST-Hash. Wenn Sie dies über den gesamten POST in einer bestimmten Reihenfolge tun (so dass jede Neuanordnung rückgängig gemacht wird), ist der Overhead minimal.

  

All dies setzt voraus, dass Sie die Post-Nachricht nicht überprüfen können, um zu sehen, ob Felder fehlen und is_set == True, length & gt; 0,! Leer () ...

    
puredevotion 08.12.2013 20:02
quelle
0

Ich denke, was Sie suchen, ist $ HTTP_RAW_POST_DATA , das wird Ihnen das geben echte POST-Länge und dann können Sie es mit $ _SERVER ['CONTENT_LENGTH'] vergleichen.

    
Pilingo 09.12.2013 22:50
quelle
0

Ich glaube nicht, dass es möglich ist, die ursprüngliche Inhaltsgröße von $ _REQUEST superglobal zu berechnen, zumindest für mehrteilige / Formulardatenanforderungen.

Ich würde Ihrer HTTP-Anfrage einen benutzerdefinierten Header mit allen parameter = Wert-Hash hinzufügen, der serverseitig überprüft werden soll. Header werden sicher ankommen, so dass Ihr Hash-Header immer da ist. Stellen Sie sicher, dass Parameter in der gleichen Reihenfolge verknüpft werden, da andernfalls der Hashwert anders ist. Achten Sie auch auf Codierung, muss auf Client und Server identisch sein.

Wenn Sie Apache konfigurieren können, können Sie einen vhost mit mod_proxy hinzufügen, der so konfiguriert ist, dass er auf einem anderen vhost auf demselben Server als Proxy fungiert. Dies sollte unvollständige Anfragen filtern. Beachten Sie, dass Sie auf diese Weise 2 Sockets pro Anfrage verschwenden. Behalten Sie also die Ressourcennutzung im Auge, wenn Sie so denken.

    
Ghigo 10.12.2013 11:12
quelle
0

Eine andere Lösung, die nützlich sein könnte ... Wenn die Verbindung von der anderen Seite langsam ist, entfernen Sie einfach das Limit für die Ausführung des Posts.

%Vor%

Und Sie werden sicher sein, dass die Lochpostdaten gesendet werden.

    
ventsi.slav 10.12.2013 16:27
quelle
0

Wenn das Berechnen der Inhaltslänge nicht sinnvoll ist, könnten Sie wahrscheinlich die vom Client gesendeten Daten signieren.

Unter Verwendung von JavaScript serialisieren Sie die Formulardaten in einer vernünftig vernünftigen Art und Weise in eine JSON-Zeichenfolge oder ein Äquivalent (d. h. sortieren Sie sie nach Bedarf), bevor Sie sie übergeben. Hash diese Zeichenfolge mit einem oder zwei recht schnellen Algorithmen (z. B. crc32, md5, sha1), und fügen Sie diese zusätzlichen Hash-Daten zu dem, was als Signatur gesendet werden soll.

Entfernen Sie auf dem Server diese zusätzlichen Hash-Daten aus der $ _POST-Anfrage und wiederholen Sie die gleiche Arbeit in PHP. Vergleichen Sie die Hashes entsprechend: Nichts geht in der Übersetzung verloren, wenn die Hashes übereinstimmen. (Verwenden Sie zwei Hashwerte, wenn Sie das winzige Risiko, falsch positive Ergebnisse zu erhalten, aufheben möchten.)

Ich wette, dass es ein vernünftiges Mittel gibt, etwas Ähnliches für Dateien zu tun, z.B. ihren Namen und ihre Größe in JS holen und diese zusätzlichen Informationen zu den Daten hinzufügen, die signiert werden.

Dies hängt etwas mit dem zusammen, was einige PHP-Frameworks tun, um Manipulationen an Sitzungsdaten zu vermeiden, wenn diese in clientseitigen Cookies verwaltet und gespeichert werden. Daher finden Sie wahrscheinlich im späteren Kontext leicht verfügbaren Code .

Ursprüngliche Antwort:

Soweit mir bekannt ist, besteht der Unterschied zwischen dem Senden einer GET- oder einer POST-Anfrage mehr oder weniger auf Mengen, die etwas wie:

senden %Vor%

vs Senden von etwas wie:

%Vor%

Sie können also für jeden Teil die Länge berechnen und diese gegen die Länge überprüfen, die durch den Inhalt-Länge-Header angekündigt wird.

  • $_FILES Einträge haben ein praktisches Größenfeld, das Sie direkt verwenden können.
  • Erstellen Sie für $_POST data die gesandte Abfragezeichenfolge neu und berechnen Sie deren Länge.

Zu beachtende Punkte:

  1. Sie müssen wissen, wie die Daten in einigen Fällen erwartet werden, z. var[]=foo&var[]=baz vs var[0]=foo&var[1]=baz
  2. In letzterem Fall handelt es sich um die C-String-Länge und nicht um die Multibyte-Länge. (Obwohl ich nicht überrascht wäre zu erfahren, dass sich ein seltsamer Browser hier und da uneinheitlich verhält.)

Weiterführende Literatur:

Denis de Bernardy 10.12.2013 11:50
quelle
0

Dies ist ein bekannter Bug in PHP und muss dort behoben werden - Ссылка

    
mp_de 12.12.2017 15:15
quelle
-1

Verwenden Sie die Ausgabepufferung mit ob_start (). Während die Ausgabepufferung aktiv ist, wird keine Ausgabe vom Skript (außer den Headern) gesendet, stattdessen wird die Ausgabe in einem internen Puffer gespeichert.

Der Inhalt dieses internen Puffers kann mit ob_get_contents () in eine String-Variable kopiert werden. Verwenden Sie ob_end_flush (), um auszugeben, was im internen Puffer gespeichert ist. Alternativ verwirft ob_end_clean () den Pufferinhalt automatisch.

    
Rajiv Charan Tej K 10.12.2013 18:14
quelle