php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Request #50210 apache: PHP won't parse multipart/form-data if it was originally chunk encoded.
Submitted: 2009-11-17 22:46 UTC Modified: 2021-10-24 04:22 UTC
Votes:11
Avg. Score:3.9 ± 1.2
Reproduced:9 of 10 (90.0%)
Same Version:2 (22.2%)
Same OS:5 (55.6%)
From: tyler dot thackray at gmail dot com Assigned: cmb (profile)
Status: No Feedback Package: Apache2 related
PHP Version: 5.2.11 OS: Unix (probably all)
Private report: No CVE-ID: None
 [2009-11-17 22:46 UTC] tyler dot thackray at gmail dot com
Description:
------------
When POSTing multipart/form-data to a PHP script, PHP will automatically parse the data into $_FILES and $_POST super globals. In this case, php://stdio will be empty. 

Apache 2 supports chunked encoded requests. When sending multipart/form-data with "transfer-encoding: chunked" Apache 2 will dechunk the request and pass it onto PHP. However, in this case PHP will NOT parse the multipart/form-data and the RAW data can be seen at php://stdio. I've examined the headers and the only difference between the working and not working is the "transfer-encoding: chunked"; the "content-type: multipart/form-data, boundry=XXXX" is still present. 

Perhaps this is an issue with mod_php? Perhaps the chunked header is confusing PHP? In any case, I would expect PHP to parse the multipart/form-data regardless if it was originally chunked or not, as Apache should take care of the chunks.

It's interesting to note that the headers can be combined with php://stdio and resent to the same script (not chunked but still multipart/form-data encoded) and PHP will actually parse it.

There's no easy way to provide sample code for this issue, suffice it to say that if you send multipart/form-data using chunked encoding to any PHP script, you will find that $_FILES and $_POST are empty and the raw data is present at php://stdio.
 

Reproduce code:
---------------
<?php
    print_r($_FILES);
    print_r($_POST);
?>

Expected result:
----------------
"Array() Array()"

Actual result:
--------------
The $_FILES and $_POST super global should contain parsed data.

Patches

Pull Requests

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2009-11-17 22:49 UTC] tyler dot thackray at gmail dot com
Excuse me, I accidentally mixed up the Expect and Actual result.
 [2009-11-18 16:50 UTC] jani@php.net
Need reproduce script. Uploading simple form with files works just fine for me under Apache2..
 [2009-11-18 17:01 UTC] tyler dot thackray at gmail dot com
A simple HTML form would not transfer the data as "transfer-encoding: chunked". Unfortunately, it requires 3rd party software to reproduce this error short of creating a complex PHP script that can simulate both chunked and multipart.

I will spend some time today on a socket-based example which sends data from one page to another, but it is less than simple. The test I have is fairly extensive, but I'll attempt to dumb it down.
 [2009-11-18 20:27 UTC] tyler dot thackray at gmail dot com
Here is the test code, please excuse its length but this is not a simple scenario. The test involves two scripts "sender.php" and "receiver.php". 

RECEIVER.PHP:
<?php
	$input = file_get_contents('php://input');
	$stdin = file_get_contents('php://stdin');

	print "FILES: ";
	print_r($_FILES);
	
	print("<br>POST: ");
	print_r($_POST);
	
	print("<br>input: ".$input);
	print("<br>stdin: ".$stdin);
?>

SENDER.PHP
<?php
	// when 'true' multipart data is NOT parsed, but is present at php://stdio
	// when 'false' multipart is parsed into $_FILES and $_POST
	$chunked = false;

	$body1 =
	"--AaB03x\r\n".
	"Content-Disposition: form-data; name=\"forPOST\"\r\n".
	"\r\n".
	"1257880790\r\n".
	"--AaB03x\r\n";
	
	$body2 =
	"Content-Disposition: form-data; name=\"test_file\"; filename=\"test.file\"\r\n".
	"Content-Type: application/octet-stream\r\n".
	"\r\n".
	"binary data\r\n".
	"--AaB03x--";
	
	// change the POST to the location of your "receiver.php"
	$header = 
	"POST /test/receiver.php HTTP/1.1\r\n".
	"Connection: close\r\n".
	"Host: ".$_SERVER['HTTP_HOST']."\r\n".
	"Content-Type: multipart/form-data, boundary=AaB03x\r\n";
	
	if ($chunked){
		$body = dechex(strlen($body1))."\r\n".$body1."\r\n".
				dechex(strlen($body2))."\r\n".$body2."\r\n0\r\n\r\n";	
		$header .= "Transfer-Encoding: chunked\r\n";
	}
	else{
		$body = $body1 . $body2;
		$header .= 	"Content-Length: ".strlen($body)."\r\n"; 	
	}
	$header .= "\r\n";

	$final = $header . $body;
	print "<pre>".$final."<br><br>";
	
	$fp = fsockopen($_SERVER['HTTP_HOST'], 80, $errno, $errstr, 30);
	if (!$fp) {
		echo "$errstr ($errno)<br />\n";
	} 
	else {
		fwrite($fp, $final);
		while (!feof($fp)) {
			print fgets($fp, 128);
		}
		fclose($fp);
	}
	print "</pre>";
?>
 [2009-12-09 22:39 UTC] tyler dot thackray at gmail dot com
I haven't gotten any feedback regarding this issue in some time. Sorry, that code is the most compact I can make it while still showing the issue. Please let me know if you need anything else to evaluate this bug.
 [2010-03-11 05:57 UTC] evert at rooftopsolutions dot nl
I believe I might have ran across the same problem (logged as #51191).

In my case the problem is specific to using the FastCGI sapi. It doesn't occur using mod_php.
 [2010-11-24 10:35 UTC] jani@php.net
-Package: Feature/Change Request +Package: Apache2 related
 [2020-12-23 22:16 UTC] shustrov dot r at gmail dot com
I am using apache2 with mod_php.
Sender script:

<?php
$response = [
    "POST /api/responses HTTP/1.1",
    "Host: 1vsig-test01.dev.aorti.tech:8080",
    "Authorization: Explicit kraken@domain.ru",
    "Accept: */*",
    "Accept-Encoding: gzip, deflate",
    "User-Agent: Python/3.5 aiohttp/3.6.2",
    "Content-Type: multipart/form-data; boundary=48d383065e754030ae01de21a3269bd5",
    "Transfer-Encoding: chunked",
    "",
    "24",
    "--48d383065e754030ae01de21a3269bd5",
    "",
    "62",
    "Content-Type: application/json",
    "Content-Length: 2",
    "Content-Disposition: form-data; name=\"meta\"",
    "",
    "",
    "2",
    "{}",
    "2",
    "",
    "",
    "26",
    "--48d383065e754030ae01de21a3269bd5--",
    "",
    "0",
    ""
];
$fp = stream_socket_client("tcp://1vsig-test01.dev.aorti.tech:8080", $errno, $errstr, 30);
if (!$fp) {
    echo "$errstr ($errno)<br />\n";
} else {
    foreach($response as $responseLine){
        fwrite($fp, $responseLine."\r\n");
    }
    while (!feof($fp)) {
        echo fgets($fp, 1024);
    }
    fclose($fp);
}


If I use the "Content-Type: multipart/form-data; boundary=48d383065e754030ae01de21a3269bd5" header, I completely lose the request body.
$input = file_get_contents('php://input');
$stdin = file_get_contents('php://stdin');
$input and $stdin - empty

If I remove "boundary=48d383065e754030ae01de21a3269bd5" and I have a "Content-Type: multipart/form-data".
then the request body exists, although not in a structured form
 [2021-10-13 15:58 UTC] cmb@php.net
> // when 'true' multipart data is NOT parsed, but is present at php://stdio
> // when 'false' multipart is parsed into $_FILES and $_POST

I cannot reproduce this; for me, $_FILES and $_POST are populated
either way.  Maybe this issue has been fixed in the meantime, or
can anybody still reproduce this with any of the actively
supported PHP versions[1]?

[1] <https://www.php.net/supported-versions.php>
 [2021-10-14 09:35 UTC] cmb@php.net
-Status: Open +Status: Feedback -Assigned To: +Assigned To: cmb
 [2021-10-24 04:22 UTC] php-bugs at lists dot php dot net
No feedback was provided. The bug is being suspended because
we assume that you are no longer experiencing the problem.
If this is not the case and you are able to provide the
information that was requested earlier, please do so and
change the status of the bug back to "Re-Opened". Thank you.
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Fri Nov 29 08:01:30 2024 UTC