Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to check for incomplete POST request in PHP

I am facing a problem when a remote web client with slow connection fails to send complete POST request with multipart/form-data content but PHP still uses partially received data to populate $_POST array. As a result one value in $_POST array can be incomplete and more values can be missing. I tried to ask same question in Apache list first and got an answer that Apache doesn't buffer the request body and passes it to PHP module as a giant blob.

Here is my sample POST request:

POST /test.php HTTP/1.0
Connection: close
Content-Length: 10000
Content-Type: multipart/form-data; boundary=ABCDEF

--ABCDEF
Content-Disposition: form-data; name="a"

A
--ABCDEF

You can see that Content-Length is 10000 bytes, but I send just one var a=A.

The PHP script is:

<?php print_r($_REQUEST); ?>

Web server waits for about 10 seconds for the rest of my request (but I don't send anything) and then returns this response:

HTTP/1.1 200 OK
Date: Wed, 27 Nov 2013 19:42:20 GMT
Server: Apache/2.2.22 (Debian)
X-Powered-By: PHP/5.4.4-14+deb7u3
Vary: Accept-Encoding
Content-Length: 23
Connection: close
Content-Type: text/html

Array
(
     [a] => A
)

So here is my question: How can I verify in PHP that the post request was received completely? $_SERVER['CONTENT_LENGTH'] would show 10000 from the request header, but is there a way to check the real content length received?

like image 457
spatar Avatar asked Nov 27 '13 23:11

spatar


Video Answer


2 Answers

I guess that the remote client is actually a browser with HTML page. otherwise, let me know and i'll try to adapt my solution.

You can add field <input type="hidden" name="complete"> (for example) as the last parameter. in PHP check firstly whether this parameter was sent from client. if this parameter sent - you can be sure that you got the entire data.

Now, i'm not sure that the order of parameters must be preserved according the RFC (of both, HTML and HTTP). but i've tried some variations and i saw that the order kept indeed.

Better solution will be, calculate (on client side) hash of the parameters and send him as another parameter. so you can be absolutely sure that you got the entire data. But this is starting to sound complicated...

like image 109
MeNa Avatar answered Oct 05 '22 22:10

MeNa


As far as I know there is no way to check if the size of received content matches the value of the Content-Length header when using multipart/form-data as Content-Type, because you cannot get hold of the raw content.

1) If you can change Content-Type (to application/x-www-form-urlencoded for example) you can read php://input, which will contain the raw content of the request. The size of php://input should match Content-Length (assuming the value of Content-Length is correct). If there's a match, you can still use $_POST to get the processed content (regular post data). Read about php://input here.

2) Or you can serialize the data on the client and send it as text/plain. The server can check the size the same way as described above. The server will need to unserialize the received content to be able to work with it. And if the client generates a hash of the serialized data and send it along in a header (X-Content-Hash for example), the server can also generate a hash and check if it matches the one in the header. You won't need to check the hash, and can be a 100% sure the content is correct.

3) If you cannot change Content-Type, you'll need something different from size to verify the content. The client could use an extra header (something like X-Form-Data-Fields) to sum up the fields/keys/names of the content you're sending. The server could then check if all fields mentioned in the header are present in the content.

4) Another solution would be for the client to have a predefined key/value as last entry in the content. Something like:

--boundary
Content-Disposition: form-data; name="_final_field_"

TRUE
--boundary--

The server can check if that field is present in the content, if so the content must be complete.

update

When you need to pass binary data, you can't use option 1, but can still use option 2:

The client can base64 encode the binary entries, serialize the data (with any technique you like), generate a hash of the serialized data, send the hash as header and data as body. The server can generate a hash of the received content, check the hash with the one in the header (and report a mismatch), unserialize the content, base64 decode the binary entries.

This is a bit more work then plainly using multipart/form-data, but the server can verify with a 100% guarantee the content is the same as what the client sent.

like image 38
Jasper N. Brouwer Avatar answered Oct 06 '22 00:10

Jasper N. Brouwer