CronJob Friendly PHP redirect - php

I'm struggling with a PHP script that is activated by a Cronjob and includes a redirect.
The script finds records in my database, sends an email as appropriate and then redirects to a compiled URL so the details of the records can be imported into an external system.
The email part works fine via a Cronjob, the redirect does not, I understand that this is because Cronjobs do not operate within the browser but I'm not sure what is the best alternative method of redirecting to the URL.
Simplified code below:
<?php
// Connect to DB
require_once($_SERVER['DOCUMENT_ROOT']."/admin/inc/dbconnect.php");
// Check DB for quotes that have not been sent to this client
$query_quotes = "SELECT * FROM quotes WHERE sent_client = 0 LIMIT 1";
$view_request = mysqli_query($GLOBALS['db_connect'], $query_quotes);
// Send to external system and email customer with quote
while ($quotes = mysqli_fetch_array($view_request)){
$Body = "Email Content";
// Send email via Swift Mailer
// Send quote information to third party system via URL redirect
header('Location: http://exampleurl/?FirstName='.rawurlencode($quotes['name']).'&businessName='.rawurlencode($quotes['company_name']).'.');
}
?>

It is as you said, you are not in a web-environment, so there is no browser to act on the Location: redirect. That doens't mean you can't pass information to a webpage - you just can't redirect to it - you call to it, and get information back - the info you get back might not be much - just 'OK, I got it' may well be more than enough. It might be as simple as
$url = "http://exampleurl/?FirstName=" .
rawurlencode($quotes['name']) . '&businessName=' .
rawurlencode($quotes['company_name'])
$response = file_get_contents($url);
And the response might be as simple as 200 OK (though if there is no response apart from just 'OK', officially according to the HTTP specs, you'd return 204 No Content, but that is a very small thing).
Better still would be to use a proper HTTP calling function that dcan deal with potential issues like redirections and make life a little easier - something like the Guzzle library for example, but again, if file_get_contents works for you with a URL, that may be enough, at least while you are testing the basics.

Related

How to get webhook data into query string to be read by a custom php script?

I have a custom script in php that is intended to receive a webhook from a 3rd party CMS (specifically FLG360), search the source for records that match a given field name in the query string (using $_GET), and return/display the results.
When going to the full URL (including the query string) manually, the query string is read by the script and the correct actions are performed.
However, when sending the webhook from the CMS, it does not seem to be able to find or read the query string.
Here's the part of my script that I expected to be reading and actioning the webhook via simple $_GET requests:
$IDtoSearch = $_GET['company'];
$status = $_GET['status'];
Is pulling data from a webhook via php as simple as performing some $_GET requests? I have confirmation from the CMS support team that the webhook is definitely formatted as a query string.
I have added this to the script, in order to record any existing query string to the server's error log:
//capture the webhook and save to error log
$webhookContent = "";
$webhook = fopen('php://input' , 'rb');
while (!feof($webhook)) {
$webhookContent .= fread($webhook, 4096);
}
fclose($webhook);
error_log($webhookContent);
...which is doing its job. Here's what I find in the logs after firing a webhook from the CMS:
eventtype=workflow&eventdatetime=2016-07-29+15%3A16%3A06&eventuserid=&eventusername=&id=109355632&subid=&ipaddress=217.33.80.130&received=2016-06-17+15%3A50%3A37&leadgroupid=49625&leadgroupname=zzALEX+TEST+Applicants+collections&leadtype=FIDOR+Applicant&status=Pre+Default+Applicant+%2336731&progress=Accepted&siteid=16515&sitename=FIDOR+Applicant&userid=32197&username=Aimee+Davies&buyerid=&buyername=&buyerreference=&introducerid=46437&introducername=Alex+Test+cases&reference=&source=FIDOR&medium=&term=&cost=0.00&value=0.00&transferdatetime=0000-00-00+00%3A00%3A00&transfersuccessful=No&xmldatetime=0000-00-00+00%3A00%3A00&xmlfails=0&xmlresult=&xmlreference=&appointmentdatetime=&appointmentnotes=&lastnotedatetime=0000-00-00+00%3A00%3A00&lastnote=&taskexists=No&workflowexists=Yes&dropbox=l109355632.d675cdbf4f75f7d9c6ee99a6b7034b7c%40msrvr.net&fullname=Mr.+Alex+Connor&title=Mr.&firstname=Alex&lastname=Connor&company=TFS9876543&jobtitle=&phone1=&phone2=&fax=&email=&address=&address2=&address3=&towncity=&postcode=&dob=&dobday=&dobmonth=&dobyear=&contacttime=&contactphone=Unknown&contactfax=Unknown&contactemail=Unknown&contactmail=Unknown&data1=&data2=&data3=&data4=&data5=&data6=&data7=&data8=&data9=&data10=&data11=&data12=&data13=&data14=&data15=&data16=&data17=&data18=&data19=&data20=&data21=&data22=&data23=&data24=&data25=&data26=&data27=&data28=&data29=&data30=&data31=&data32=&data33=&data34=&data35=&data36=&data37=&data38=&data39=&data40=&data41=&data42=&data43=&data44=&data45=&data46=&data47=&data48=&data49=&data50=&type1=Work+Telephone+Number&type2=TFS+Number&type3=Current+loan+balance&type4=Contracted+Monthly+Payment+Amount&type5=Last+payment+date+received&type6=Last+amount+received&type7=Next+scheduled+payment+date&type8=Current+Arrears+figure+incl+Legals%2FCharges&type9=Current+Payment+method&type10=Guarantor+is+deceased&type11=Guarantor+has+entered+DMP%2FBKO%2FIVA+&type12=Spare&type13=Actual+receipts+to+date&type14=Applicant+flat+number&type15=Applicant+house+name&type16=Applicant+house+number&type17=Guarantor+relationship+to+main+applicant&type18=Guarantor+First+Name&type19=Guarantor+Last+Name&type20=Joint+App+First+Name&type21=Joint+App+Last+Name&type22=Applicant+has+entered+DMP%2FBKO%2FIVA&type23=Current+Vienna+Status&type24=Solicitors&type25=Legal+Status&type26=Number+of+Months+in+Arrears&type27=Promise+To+Pay+DATE+%28dd%2Fmm%2Fyyyy%29&type28=Promise+To+Pay+AMOUNT&type29=Loan+Final+payment+date+%28dd%2Fmm%2Fyyyy%29&type30=Agreement+signed+date+%28FOR+DEFAULT%29+-+dd%2Fmm%2Fyyyy&type31=Default+expiry+date+%28dd%2Fmm%2Fyyyy%29&type32=Less+Rebate+of+interest+%28amount+from+settlement+letter%29&type33=Sum+Required+to+repay+loan+%28Amount+from+settlement+letter%29&type34=Default+ISSUED+date+%28dd%2Fmm%2Fyyyy%29&type35=Arrangement+Amount&type36=Arrangement+Start+Date+%28dd%2Fmm%2Fyyyy%29&type37=Arrangement+Type&type38=Arrangement+Term&type39=Arrangement+Monthly+Shortfall&type40=spare&type41=spare&type42=spare&type43=spare&type44=spare&type45=DMP%2FIVA+offer+of+Repayment&type46=Reasons+for+Arrears&type47=OFS+LETTER+3+DATE+%28dd%2Fmm%2Fyyyy%29&type48=IBC+Referance&type49=Restriction+or+Charging+order&type50=IMPORTANT+NOTES
All the information from the CMS record seems to be coming in, just not being read or recognised as a query string by my script.
Here's what I get when manually entering a query string on the URL:
, referer: https://www.domain.com/flg/index.php/?company=cream&status=cheese
Not sure what the ", referer:" part means, or why this error is being logged with the full URL as opposed to the above.
Hope somebody can help with this, I'm tearing my hair out.
Please let me know if you need any additional information.
Many thanks.
U can use parse_str() on $webhookContent
Credit to #Aleksej in the comments for this - it was as simple as changing the GET to POST. Problem solved, the script is now doing its job.

PHP: help secure / filter ajax calls

I am trying to find a way to filter ajax calls in order to add a fine layer of security to my applications. Does the code bellow make any sense?
function is_ajax(){//Help Secure Ajax Calls
if(isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH']=='XMLHttpRequest') return;
else die;//no ajax
}
My dream is only let a file inside my server (htm or php) to access another php file via ajax.
I wonder if the code bellow would not do better:
if(strpos($_SERVER['REQUEST_URI'],'http://')) die;//Help Secure From URL Include Attacks
Thanks.
Since the AJAX call is always made from the browser, the request is not coming from your own server, but the client machine. Even if you set custom headers, these can easily be manipulated on the client side.
If your goal is to only allow your own scripts to access the script containing the ajax content, I'd recommend generating a token string that is only valid for a certain requested url and a specified time.
Quick and dirty example:
Script requesting the AJAX resource:
$secret ="ABC1232";
$item = array(
"time"=>time(),
"token_id"=>"<page_url>"
);
$signed = base64_encode(hash_hmac("sha256",json_encode($item),$secret));
$item = base64_encode(json_encode($item));
$ajax_url = "myscript.php?signed=$signed&item=$item";
AJAX Resource, check the token is valid
$item = json_decode(base64_decode($_REQUEST["item"]));
$timeout = 3600;
if($item->time < (time()-$timeout)){
die("Invalid token - timeout");
}
if($item->token_id !== "<page_url>"){
die("Invalid token - page url");
}
$secret ="ABC1232";
$valid = ($_REQUEST["signed"] === base64_encode(hash_hmac("sha256",json_encode($item),$secret));
if(!$valid){
die("Invalid token");
}
Your Ajax is as secure as your application layer. Ajax is just client side code accessing server side code. There is probably no way in securing it the way you want since anyone can emulate Ajax calls from within any language by just modifying the request headers. One more thing most ajaxified frameworks do include HTTP_X_REQUESTED_WITH but it's not 100% reliable.
Just secure your server code and your good to go. If you really want to at least let your server code know that it's receiving an ajax call, use a parameter on the request.
Assume that anything that can be requested by AJAX will also be accessed directly and you will be in a much safer place. You really should consider why you would only ever want something to be accessed via AJAX in the first place. The HTTP_X_REQUESTED_WITH header is unreliable and easily spoofed so it provides no real security anyway.
I think for a little more security you could decode your secret string after you encode it. then echo than decoded string in php and then copy it.
then on the the $valid variable
$valid = (base64_decode($_REQUEST["signed"]) === "blahblahblah that you copied from php");

Posting and executing a string as PHP?

I'm building an application that's self-hosted, and I'd like to have some form of licensing to minimize fraudulent downloads/distribution.
Of course, I'm well aware that being self-hosted someone could simply rip out all license features from the source-code, but the con's of using a compiler like Zend Guard or ionCube far outweigh the pro's in my opinion - nonetheless I'd like to have some basic form of license security.
What I originally had in mind to do was: user logs in with license on app -> app posts license to my server -> server sends a response via a HTTP GET request -> app evaluates response, and if license is valid sets a value in a session variable (A), if invalid returns to login screen with an error.
The problem with this is, the evaluation of response/session setting is readily available in a application file, so if the user knows a little PHP and checks in on that source code, they'll realize all they'll need to do is set a session themselves with a particular $_SESSION['_valid_license'] value, and they'll be good to go.
What I was considering doing to make it a little less easy was (if possible) to post PHP back as a response, and then have the application file execute it, for example:
My original code:
$response = $_GET['response'];
if($response == "fjdkuf9") {
start_session();
$_SESSION['_valid_license'] = "YES";
header("Location:" . $rp . "/admin/");
} else {
header("Location:" . $rp . "/login/?err=1");
}
My new concept:
$response = $_POST['response'];
str_replace("\", "", $response);
With the following being posted as response:
start_session();
\$_SESSION[\'_valid_license\'] = \"YES\";
header(\"Location:\" . \$rp . \"/admin/\");
Would that execute $response as actual PHP code after str_replace()? If possible, this would be great, as it would mean evaluation would be done on my server rather than within the self-hosted app itself.
Your second solution is just as insecure as the first. here's what I would do:
Your application POSTS to your server a serial number or some other identifying information.
Your server validates the serial number against the user's account or whatever and returns a unique response.
If that response is successful, you allow the user to continue. Obviously you'd want to implement some sort of caching mechanism here so you're not having to hit you server on every page view.
Putting the responsibility of validation on your server instead of self-hosted code is much more secure. You would need to encrypt the data that is sent BTW so that someone couldn't simply emulate the success response, but you get the idea.

How to retrieve http page from another host server with php and detect redirect

Let's say I have a server at www.myhost.com. From there by using php I want to retrieve the html document that the php file www.anotherhost.com/somefile.php produces. I also want to use php to do this. anotherhost is not on the same server as myhost.
I know that functions such as fopen, f_get_contents and file can do this by simply executing e.g.
$fp = fopen ("http://www.anotherhost.com/somefile.php")
and from there fp can be used as any other file pointer to read the contents.
BUT, for some reason I also want to know if somefile.php at anotherhost ordered a client-side redirect when I tried to retrieve somefile.php´s resulting html document. somefile.php can do this by
header ("Location: http://www.anotherhost.com/extrafile.php")
Now fopen will retrieve the html document that extrafile.php produces, without detecting that a client-side redirect has been performed.
Is there some functionality in PHP that enables you to retrieve html documents from other servers AND notifies you if a redirect has taken place? It is acceptable if I must follow the redirect by myself (not done automatically), as long as I'm told what the new URL is.
Executing arbitrary commands with the function system is not preferred.
PS. If you are going to suggest fsockopen, then please explain why i get the error "Unable to find the socket transport "http" - did you forget to enable it when you configured PHP?" when I try to execute
fsockopen ("http://localhost")
and I also get "Failed to parse address "localhost" when I do
fsockopen ("localhost")
Thanks for reading. Help would be greatly appreciated.
You can use the Zend_Http_Client from Zend Framework. It has methods for retrieving the number of redirects as well as setting a limit to the number of redirects to follow etc. If you just want to know how it's done, then you can look into the source code and try to figure it out. Shouldn't be too hard I think.
I would use cURL for this. If the redirect is specified in the header, instead of apache mod_rewrite for example, then you should be able to detect if a redirect is present by grabbing all of the headers sent by the response.
Optionally, there is a setting to automatically follow the redirect (http://us.php.net/manual/en/function.curl-setopt.php).
As of php 5.2 you can use the stream api and a stream_notification_callback
e.g.
function notification($code, $severity, $message, $mcode, $transferred, $max) {
if ( STREAM_NOTIFY_REDIRECTED===$code ) {
echo 'redirected to ', $message, "\n";
}
}
$ctx = stream_context_create(null, array('notification' => 'notification'));
$c = file_get_contents("http://spiegel.de", false, $ctx);
echo 'len=', strlen($c);
prints
redirected to http://www.spiegel.de/
len=144446
You probably want a full HTTP client, not stream wrappers and fopen()/file_get_contents(). I think its possible to do what you want with stream wrappers using stream_get_meta_data() but you would be better off with a fuller implementation.
If you don't want to go down the Zend or stream route, you could always use the cURL functions. These will let you return the headers, hence you could analyse these to ascertain whether any re-directs are being issued.
(You can also use the 'CURLOPT_MAXREDIRS' option to specify that it shouldn't follow re-directs.)

How would I go about writing a simple PHP bot?

How would I go about writing a simple PHP bot that could login in and recieve all the cookies sent by the server? ... and then send them back when required?
Any suggestions would be appreciated.
First of all, your question is too broad and lacking in detail to really answer effectively. That said, I'll give it a try.
Not knowing what exactly you mean by "log in", I assume you want the script to be able to post some data to another script via an HTTP request. The CURL Library is good for that. It is able to post data and handle cookies.
Edit: Got ninja'd by Zed. ;)
If for some reason you cannot use the curl extension on your server (extension not installed), you can use a class such as Snoopy which will still allow you to either use the curl binaries or use sockets to retrieve the information.
Snoopy handles cookies.
As for the writing the bot itself, it's just a question of sending the proper requests. Here is an example with Snoopy:
$snoopy = new Snoopy;
// The following needs to reflect the form configuration of the site
$login = array('usr' => 'hi', 'pwd' => 'hello');
if($snoopy->submit('http://example.com/login', $login) === false) {
// output the response code
die($snoopy->response_code . ':' . $snoopy->error);
}
//Request succeeded (doesn't mean we are logged in)
// output the results
echo $snoopy->results;
// Check the results to see if you are logged in and
// Continue using $snoopy.
// It will pass the proper cookies for the next requests.
With the help of the cURL library?

Categories