I've been stuck on this problem for several hours: I'm using PHP and cURL to write a sort of PHP proxy. Almost everything works fine, setting cookies, handling redirects, and submitting forms using POST.
Basically, I'm trying to mirror a remote website with my local proxy. To do so, I redirect every request to http://localhost/resource to http://localhost/proxy.php?url=http://remotesite.com/resource that will fetch the resource on the remote website. The redirect is handled by a 404 error page on .htaccess but I guess that using mod_rewrite would not change things.
I'm testing my proxy on a complex application (the latest version of WordPress) deployed on a remote server. The WordPress login works fine and uses POST. However, I found a page where updating a form does not work, and for which all the POST data is not sent at all to the server.
Here is what I see with wireshark listening on the loopback interface:
POST /proxy/wp-admin/media.php?attachment_id=691&action=edit HTTP/1.1
Host: localhost
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2.23) Gecko/20110921 Ubuntu/10.04 (lucid) Firefox/3.6.23
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 115
Connection: keep-alive
Referer: http://localhost/proxy/wp-admin/media.php?attachment_id=691&action=edit
Cookie: [snip]
Cache-Control: max-age=0
Content-Type: application/x-www-form-urlencoded
Content-Length: 501
attachments%5B691%5D%5Bmenu_order%5D=0&attachments%5B691%5D%5Bpost_title%5D=fb&attachments%5B691%5D%5Bimage_alt%5D=&attachments%5B691%5D%5Bpost_excerpt%5D=&attachments%5B691%5D%5Bpost_content%5D=foobar&attachments%5B691%5D%5Burl%5D=http%3A%2F%2Flocalhost%2Fproxy%2Fwp-content%2Fuploads%2F2009%2F04%2Ffb.gif&save=Aggiorna+media&post_id=&attachment_id=691&action=editattachment&_wp_original_http_referer=&_wpnonce=02caf30462&_wp_http_referer=%2Fwp-admin%2Fmedia.php%3Fattachment_id%3D691%26action%3Dedit
HTTP/1.1 200 OK
Date: Wed, 19 Oct 2011 16:18:56 GMT
Server: Apache/2.2.14 (Ubuntu)
X-Powered-By: PHP/5.3.2-1ubuntu4.10
Vary: Accept-Encoding
Content-Encoding: gzip
Content-Length: 5441
Keep-Alive: timeout=15, max=100
Connection: Keep-Alive
Content-Type: text/html; charset=UTF-8
[content]
While, what I see if listening on the interface connected to the internet is:
POST /wp-admin/media.php?attachment_id=691&action=edit HTTP/1.1
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2.23) Gecko/20110921 Ubuntu/10.04 (lucid) Firefox/3.6.23
Host: www.remotesite.com
Accept: */*
Referer: http://www.remotesite.com/wp-admin/media.php?attachment_id=691&action=edit
Cookie: [snip]
X-Forwarded-For: 127.0.0.1
Content-Length: 0
Content-Type: application/x-www-form-urlencoded
HTTP/1.1 200 OK
Transfer-Encoding: chunked
Date: Wed, 19 Oct 2011 16:25:13 GMT
Server: LiteSpeed
Connection: close
X-Powered-By: PHP/5.2.17
Expires: Wed, 11 Jan 1984 05:00:00 GMT
Last-Modified: Wed, 19 Oct 2011 16:25:13 GMT
Cache-Control: no-cache, must-revalidate, max-age=0
Pragma: no-cache
X-Frame-Options: SAMEORIGIN
Content-Type: text/html; charset=UTF-8
[content]
As you see my proxy is not transmitting the post data to the remote server.
I expect the problem to be related to the encoding of the POSTDATA, as POST variables in this case are in an array (attachments[691][menu_order]=0; attachments[691][post_content]=foobar and so on...).
I tried several changes as suggested by similar posts but haven't managed to change the behavior of the script at all. All this because apparently the first (local) POST sends the data to localhost, but cURL is unable to fetch the POST data (indeed, file_get_contents("php://input") in the code below reads 0 bytes).
I paste here part of my code hoping somebody can help me:
$ch = curl_init( $url );
$headers = array();
if ( isset($_SERVER['CONTENT_TYPE']) ) {
// commenting this out or changing to multipart/form-data does not change anything
array_push($headers, "Content-Type: ".$_SERVER['CONTENT_TYPE'] );
}
if ( count($headers) > 0 ) {
curl_setopt( $ch, CURLOPT_HTTPHEADER, $headers );
}
$postdata = file_get_contents("php://input"); //this turns out to be empty - and so is $_POST
//REQUEST METHOD: since pages are redirected from a 404 error page, we have to handle
//a redirect, so the real method is specified in REDIRECT_REQUEST_METHOD
if ( isset($_SERVER['REDIRECT_REQUEST_METHOD']) && isset($postdata) ){
if ($_SERVER['REDIRECT_REQUEST_METHOD'] == 'POST'){
curl_setopt( $ch, CURLOPT_POST, true );
}
}
else{
if (isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] == 'POST' ){
curl_setopt( $ch, CURLOPT_POST, true );
}
}
if ( isset($_SERVER['CONTENT_LENGTH'] ) && $_SERVER['CONTENT_LENGTH'] > 0 ) {
curl_setopt( $ch, CURLOPT_POSTFIELDS, $postdata );
}
//set cookies
curl_setopt($ch, CURLOPT_COOKIEJAR, '/tmp/cookietofwd');
curl_setopt($ch, CURLOPT_COOKIEFILE,'/tmp/cookietofwd');
curl_setopt( $ch, CURLOPT_FOLLOWLOCATION, true );
curl_setopt( $ch, CURLOPT_HEADER, true );
curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true );
$out=curl_exec( $ch );
[...]
Why don't you get your POST data from $_POST? The application/x-www-form-urlencoded header appears to be an AJAX request instead of a regular POST, which I am not sure how it is handled by php://input.
You could so something like:
curl_setopt( $ch, CURLOPT_POSTFIELDS, http_build_query($_POST) );
Is there a specific reason why you want this in PHP? Why don't you just use nginx? (http://nginx.org/en/)
It will probably do a better job (and faster).
Related
I have a problem logging in to a website with CURL and PHP.
I test with the Firefox add-on HttpRequester and this worked.
Result login:
POST https://www.balatarin.com/sessions
Content-Type: application/x-www-form-urlencoded
session[login]=testeruni&session[password]=123456789&session[remember_me]=1&commit=%D9%88%D8%B1%D9%88%D8%AF&utf8=%E2%9C%93&authenticity_token[![httprequester][1]][1]
-- response --
200 OK
Server: shield
Date: Thu, 19 Jan 2017 13:51:54 GMT
Content-Type: text/html; charset=utf-8
status: 200 OK
X-Frame-Options: SAMEORIGIN
X-XSS-Protection: 1; mode=block
x-ua-compatible: IE=Edge,chrome=1
Etag: W/"7418542e936fbdfe20002faf11876845"
Cache-Control: must-revalidate, private, max-age=0
Set-Cookie: _balat_session_new=BAh7C0kiDHVzZXJfaWQGOgZFRmkD964BSSIPc2Vzc2lvbl9pZAY7AEZJIiUzZGUxMzIyN2ZhZDVmMDUzOGE3OGY0YTRhZDkzNmUyMQY7AFRJIhZpbnB1dF9kZXZpY2VfdHlwZQY7AEZJIgpNT1VTRQY7AEZJIhRob3Zlcl9zdXBwb3J0ZWQGOwBGVEkiCmZsYXNoBjsARm86JUFjdGlvbkRpc3BhdGNoOjpGbGFzaDo6Rmxhc2hIYXNoCToKQHVzZWRvOghTZXQGOgpAaGFzaHsGOgtub3RpY2VUOgxAY2xvc2VkRjoNQGZsYXNoZXN7BjsKSSI22YbYrtiz2Kog2YXYtNiq2LHaqSDahtmG2K8g2KjYp9mE2KfahtmHINi02YjbjNivLgY7AFQ6CUBub3cwSSIQX2NzcmZfdG9rZW4GOwBGSSIxT3krNk5nM1NTM2IreXc4SUtxbW9yN2NmMXQrdUNLWWdubFRRYmpidmtNTT0GOwBG--2c2a72f8ec27564250ba084d97998aefba4af11a; path=/; secure; HttpOnly geo=0
X-Request-Id: 521288561d7cfff0ef8fe8d72080760c
X-Runtime: 0.188862
X-Rack-Cache: miss
Content-Encoding: gzip
Via: 1.1 google
Alt-Svc: clear
Expires: Thu, 19 Jan 2017 13:51:54 GMT
X-Firefox-Spdy: h2
but it does not login with curl in PHP. I tested all headers in my CURL but it does not login, only works with HttpRequester.
public function actionLoggin()
{
$url = 'https://www.balatarin.com/sessions';
$headers[] = 'Content-Type: application/x-www-form-urlencoded';
$headers[] = 'Host: www.balatarin.com';
$headers[] = 'User-Agent: Mozilla/5.0 (Windows NT 6.3; Win64; x64; rv:50.0) Gecko/20100101 Firefox/50.0';
$headers[] = 'Referer: https://www.balatarin.com/login';
$params = array(
'session[login]' => 'testeruni',
'session[password]' => '123456789',
'session[remember_me]' => '0',
'commit' => 'ورود',
'utf8' => '✓',
'authenticity_token' => '',
);
//open connection
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($params));
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_COOKIEJAR, 'bala_cookie.txt');
curl_setopt($ch, CURLOPT_COOKIEFILE, 'bala_cookie.txt');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$result = curl_exec($ch);
curl_close($ch);
echo $result;
}
Here is my cookie file:
# Netscape HTTP Cookie File
# https://curl.haxx.se/docs/http-cookies.html
# This file was generated by libcurl! Edit at your own risk.
www.balatarin.com FALSE / FALSE 0 logged_in 1
#HttpOnly_www.balatarin.com FALSE / TRUE 0 _balat_session_new BAh7CToOcmV0dXJuX3RvMDoMdXNlcl9pZGkDj60BOhJsb2dpbl9yZXRyaWVzMEkiD3Nlc3Npb25faWQGOgZFRkkiJTgwN2ZmMDRjMGUzMzkyMDIyZWY5YzBmZTQxN2FmZWMzBjsIVA%3D%3D--d47dd61bc9900449cca69ebd727041c3946a13ba
www.balatarin.com FALSE / FALSE 0 geo 0
www.balatarin.com FALSE / FALSE 1516368886 corr b8ed93fa279a469a637b
I'm trying to embed Facebook's posts (e.g. video) using oEmbed format. According to Facebook documentation, oEmbed is now supported. I'm trying this PHP code:
$json_post = #file_get_contents('https://www.facebook.com/plugins/video/oembed.json/?url={MY VIDEO URL HERE}');
$oembed = json_decode($json_post);
var_dump($oembed);
I already used the same code for Instagram with success, now I'm getting a NULL result. oEmbed works good if i directly write the URL on the browser. Am i missing something?
Thanks.
Update
I tried with Curl:
$url='https://www.facebook.com/plugins/video/oembed.json/?url=https%3A%2F%2Fwww.facebook.com%2Ffacebook%2Fvideos%2F10153231379946729%2F';
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_HEADER, TRUE);
//curl_setopt($ch, CURLOPT_NOBODY, TRUE); // remove body
curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
$page = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
print_r($page);
curl_close($ch);
Now i get:
HTTP/1.1 302 Found
Location: https://www.facebook.com/unsupportedbrowser
access-control-allow-method: OPTIONS
Access-Control-Expose-Headers: X-FB-Debug, X-Loader-Length
Access-Control-Allow-Origin: https://www.facebook.com
Vary: Origin
Access-Control-Allow-Credentials: true
Content-Type: text/html
X-FB-Debug: gGcZzyllZadlcn/6jz2HqqouIcDnhTzxzR+etWXhZEnOcditfsaIUw0WjgO3nELHzveRCYa1UM86D3LA/nLnNw==
Date: Wed, 11 Jan 2017 10:18:47 GMT
Connection: keep-alive
Content-Length: 0
HTTP/1.1 200 OK
X-XSS-Protection: 0
public-key-pins-report-only: max-age=500; pin-sha256="WoiWRyIOVNa9ihaBciRSC7XHjliYS9VwUGOIud4PB18="; pin-sha256="r/mIkG3eEpVdm+u/ko/cwxzOMo1bk4TyHIlByibiA5E="; pin-sha256="q4PO2G2cbkZhZ82+JgmRUyGMoAeozA+BSXVXQWB8XWQ="; report-uri="http://reports.fb.com/hpkp/"
Pragma: no-cache
Cache-Control: private, no-cache, no-store, must-revalidate
Expires: Sat, 01 Jan 2000 00:00:00 GMT
X-Content-Type-Options: nosniff
Strict-Transport-Security: max-age=15552000; preload
X-Frame-Options: DENY
Vary: Accept-Encoding
Content-Type: text/html
X-FB-Debug: zwArox8KyM3BtwLymhiARCTltrrcE/pDqSWdqbHgstXVBEbIXG57Od2MfDnqgqSX5Tj43qoe8uYhphzwoZcXeg==
Date: Wed, 11 Jan 2017 10:18:48 GMT
Transfer-Encoding: chunked
Connection: keep-alive
Still waiting for a reply.
Thank You.
Set the user agent with the curl and try,
$browser = 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.16 (KHTML, like Gecko) \Chrome/24.0.1304.0 Safari/537.16';
curl_setopt($ch, CURLOPT_USERAGENT, $browser);
Here is the answer with file_get_content,
$options = array(
'http'=>array(
'method'=>"GET",
'header'=>"Accept-language: en\r\n" .
"Cookie: foo=bar\r\n" . // check function.stream-context-create on php.net
"User-Agent: Mozilla/5.0 (iPad; U; CPU OS 3_2 like Mac OS X; en-us) AppleWebKit/531.21.10 (KHTML, like Gecko) Version/4.0.4 Mobile/7B334b Safari/531.21.102011-10-16 20:23:10\r\n" // i.e. An iPad
)
);
$context = stream_context_create($options);
$json_post = #file_get_contents('https://www.facebook.com/plugins/video/oembed.json/?url=https%3A%2F%2Fwww.facebook.com%2Ffacebook%2Fvideos%2F10153231379946729%2F', false, $context);
$oembed = json_decode($json_post);
var_dump($oembed)
I'm trying to upload a file via cURL but something is missing. I forces this request to be HTTP 1.0 because cURL adds the Expect: 100 header if I use HTTP 1.1 so thats why the extra header. Here is a simple test code:
<?php
if(isset($_POST["id"])) {
$data = array("id" => $_POST["id"]);
$data["file"] = "#".realpath($_FILES["file"]["tmp_name"]);
$ch = curl_init();
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
'Authorization: Bearer 0e39ffba-66cd-4933-9e94-fcdf600c2453',
'Connection: keep-alive'
));
curl_setopt($ch, CURLOPT_URL, "http://localhost:8080/test-api/upload");
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
curl_setopt($ch, CURLOPT_VERBOSE, false);
curl_setopt($ch, CURLOPT_HTTP_VERSION, 1);
$response = curl_exec($ch);
var_dump($response);
exit;
}
?>
My Jersey based server picks it up, and I can see these headers:
INFO: 25 * Server has received a request on thread http-nio-8080-exec-1
25 > POST http://localhost:8080/test-api/upload
25 > authorization: Bearer 0e39ffba-66cd-4933-9e94-fcdf600c2453
25 > connection: keep-alive
25 > content-length: 261
25 > content-type: multipart/form-data; boundary=------------------------53f7ba34739b4d9e
25 > host: localhost:8080
See the content-length? It's way too short. When I send the same file and the same request via my Postman REST client, I get these headers:
INFO: 26 * Server has received a request on thread http-nio-8080-exec-3
26 > POST http://localhost:8080/test-api/upload
26 > accept-encoding: gzip, deflate
26 > accept-language: hu-HU,hu;q=0.8,en-US;q=0.6,en;q=0.4
26 > authorization: Bearer 0e39ffba-66cd-4933-9e94-fcdf600c2453
26 > cache-control: no-cache, no-cache
26 > connection: keep-alive
26 > content-length: 144954
26 > content-type: multipart/form-data; boundary=----WebKitFormBoundarye5Tg0kEqi10nEBwv
26 > cookie: ff_uvid=126143952; _ga=GA1.1.459454356.1439469592; CAKEPHP=9mffidqo8203ugktan4roc0u82
26 > host: localhost:8080
26 > origin: chrome-extension://fdmmgilgnpjigdojojpjoooidkmcomcm
26 > user-agent: Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.80 Safari/537.36
The content-length now is set property. What could be wrong here?
It sounds like you're using PHP 5.6.0 or later. As of this release, the # prefix for file uploads is disabled by default. You can enable it with
curl_setopt($ch, CURLOPT_SAFE_UPLOAD, false);
This option was added in 5.5, but the default was false for backward compatibility; 5.6 changed the default incompatibly.
The preferred way to perform file uploads starting with 5.5 is with the CurlFile class.
$data["file"] = new CurlFile(realpath($_FILES["file"]["tmp_name"]));
You have to actually insert the filecontent, this differs from the cli-version of curl.
try:
$data["file"] = file_get_contents($_FILES["file"]["tmp_name"]);
I have a webapp that needs to be able to recreate the post actions of application provided by our vendor. The application allows the user to log in or out of phone workgroups. I have captured the HTTP Post request that the application is sending to initiate a session and to log the user in and out of the workgroups. I would like to recreate these POSTs in PHP using CURL, but I am having some issues getting the POST correct.
The post I am trying to emulate as captured from WireShark looks like this :
POST /Login?timeout=6 HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Host: 10.1.##.##:5447
Content-Length: 160
Expect: 100-continue
Connection: Keep-Alive
{"username":"joell","user-auth-token":"TOKENTOKENTOKEN","user-role":"admin_role","client-type":3,"app-id":"cmwin.18.62.7800.0"}
Response
HTTP/1.1 200 OK
Content-Length: 58
Content-Type: text/plain; charset=UTF-8
Connection: Keep-Alive
Cache-Control: no-store
Date: Fri, 03 Apr 2015 13:08:42 GMT
Expires: Fri, 03 Apr 2015 13:08:42 GMT
Access-Control-Allow-Origin: *
Set-Cookie: SessionId=2006727099
My php code atempting to recreate this is:
$data = array(
"username" => "joell",
"user-auth-token" => "TOKENTOKENTOKEN",
"user-role" => "admin_role",
"client-type" => 3,
"app-id" => "cmwin.18.62.7800.0"
);
$data_string = json_encode($data);
$curl = curl_init('http://10.1.##.##:5447/Login?timeout=6');
curl_setopt($curl, CURLOPT_CUSTOMREQUEST, "POST");
curl_setopt($curl, CURLOPT_POSTFIELDS, $data_string);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_HTTPHEADER, array(
'Content-Type: application/x-www-form-urlencoded',
'Content-Length: ' . strlen($data_string),
'Expect: 100-continue',
'Connection: Keep-Alive')
);
if(!curl_exec($curl)){
die('Error: "' . curl_error($curl) . '" - Code: ' . curl_errno($curl));
}
$result = curl_exec($curl);
print_r($result);
curl_close($curl);
The request that my script is generating is:
POST /Login?timeout=6 HTTP/1.1
Host: 10.1.##.##:5447
Accept: */*
Content-Type: application/x-www-form-urlencoded
Content-Length: 160
Expect: 100-continue
Connection: Keep-Alive
{"username":"joell","user-auth-token":"TOKETOKETOKEN","user-role":"admin_role","client-type":3,"app-id":"cmwin.18.62.7800.0"}
Response
HTTP/1.1 200 OK
Content-Length: 20
Content-Type: text/plain; charset=UTF-8
Connection: Keep-Alive
Cache-Control: no-store
Date: Fri, 03 Apr 2015 15:15:44 GMT
Expires: Fri, 03 Apr 2015 15:15:44 GMT
Access-Control-Allow-Origin: *
Currently the output of my PHP script is :
{"error":2147483650}
I want to send a request using cURL and retrieve the response header.
Using a browser the response header is as follow:
HTTP/1.0 302 Moved Temporarily
Content-Type: text/html; charset=utf-8
Cache-Control: no-cache
Location: "Correct URL"
Expires: Fri, 01 Jan 1990 00:00:00 GMT
Date: Tue, 30 Oct 2012 08:32:24 GMT
Server: Google Frontend
Content-Length: 0
But when I send the request using cURL the response header is as follow:
HTTP/1.1 302 Found
Content-Type: text/html; charset=utf-8
Cache-Control: no-cache
Location: "Wrong URL"
Expires: Fri, 01 Jan 1990 00:00:00 GMT
Date: Tue, 30 Oct 2012 09:12:14 GMT
Server: Google Frontend
Content-Length: 0
I want to know what is causing the response to return different URLs. This is a small php sample out of many samples and things I tried with no avail.
<?php
$url = "url";
$ch = curl_init( $url );
curl_setopt( $ch, CURLOPT_FOLLOWLOCATION, true );
curl_setopt( $ch, CURLOPT_HEADER, true );
curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true );
curl_setopt( $ch, CURLOPT_COOKIEJAR, "cookie.txt" );
curl_setopt( $ch, CURLOPT_COOKIEFILE, "cookie.txt" );
curl_setopt( $ch, CURLOPT_USERAGENT, "Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.1.4) Gecko/20091030 Gentoo Firefox/3.5.4" );
list( $header, $contents ) = preg_split( '/([\r\n][\r\n])\\1/', curl_exec( $ch ), 2 );
curl_close( $ch );
$header_text = preg_split( '/[\r\n]+/', $header );
foreach ( $header_text as $headers ) {
echo $headers . "</br>";
}
?>
There is some difference between the requests sent through the browser and through curl (almost certainly in the HTTP headers) that causes the difference in the responses.
You should capture the request from the browser (perhaps using an HTTP proxy like Fiddler for convenience) and compare its headers to those from your curl request. One (or more) of the differences you will find is the reason for what you are seeing.