As part of a page I'd like to be able to show files available for download if they contain the GET result number ($_GET[number]). However, what I'm doing doesn't appear to be working, and I'm also not sure it's a particularly secure means of achieving it either. Here's what I'm trying so far (which doesn't display anything at all!):
foreach (glob("Files/*$_GET[number]*.*") as $filename) {
echo $filename."<br />";
}
You definatly should check $_GET["number"] if is really and only a number for security reasons.
$_GET["number"] = intval($_GET["number"]);
Sorry but too low reputation to post this as a comment..
Related
I know this question has been asked millions of times, but please actually take the time to understand my problem before marking as duplicate or closing.
So I am using the following code. For some reason it gets all of the correct header information the first time I run the code EXCEPT for content-length. The second time I run the code it actually gets it correctly. I am retrieving the images from Facebook API if that changes anything.
function remote_filesize($url) {
$data = get_headers($url, 1);
if(isset($data['Content-Length'])) {
return (int) $data['Content-Length'];
}
else {
return -1;
}
}
Edit
Gotta love when you get downvoted with no explanation. Personally I think it should be required to provide a reason.
Anyway, this is still an issue, but in case anyone googling this needs a solution for getting the remote filesize, I would suggest using this awesome snippet from the PHP docs http://php.net/manual/en/function.filesize.php#114952
Sounds like a server caching issue.
So you may have to issue a full GET request instead of just a HEAD request.
Also, maybe check different casing -- 'content-length:' -- lowercase.
I am currently displaying errors on my website using this code:
<?php
$failure = strip_tags($_GET['failure']);
if($failure!=""){
echo '<div class="error">';
echo $failure;
echo '</div>';
}
?>
However, I am curious if this is safe. Is it?
while this solution would change a little bit your approach, why not have an errors.php file with this structure?
$error[1] = 'some error message';
$error[2] = 'some other error message';
$error[3] = '...'; // you get the point
And the just send an ID as the error:
somepage.php?failure=2
Then, include this code where you usually display your errors:
if($_GET['failure'] && array_key_exists($_GET['failure'],$error) {
echo $error[$_GET['failure']];
}
Just be sure to include errors.php in your config.php file (or whatever your main configuration's file name is).
Why?
Errors are often repeated, this way you can use them over and over again.
If you want to translate the site to another language, this system will be very helpful.
If you need to change a word on an error message, you will just have to change it once in the errors.php file.
IMHO it is much much safer to use ints than strings in this case.
You can (and of course always should, hat tip #DaveRandom) do a htmlspecialchars() after strip_tags in order to prevent some clever construction from getting past the tag stripper. I have never seen a working exploit doing that successfully but it can't hurt taking additional precautions.
If you do all that, this looks safe.
Note that there is a limit on the maximum size of a GET request - 1kb is a safe maximum amount.
Depending on what version of PHP you have, filter_input() is a decent alternative.
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>
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 !
At the end of a page, if something occurs, it needs to be cleared, then the entire page needs to be re-parsed before serving to the client. I was going to echo out a javascript to refresh the page, but that will make them load the page and then reload it...I was wondering if there was a way to just tell the php engine to go back to the beginning and re-parse the entire page?
Thanks!
I will try to explain the problem more clearly but it is complicated and I am a terrible communicator. I on the page that lists products I am giving users the option to select fields to narrow the results. The system remembers this so they don't have to keep selected them. If they narrow a category like metal color and then go to a category that metal color is irrelevant like crystal figurines it will not show any results because none will match the metal color chosen. To generate the query to pull the products from the data-base is very complicated because different categories have different requirements to find the correct products. so once the query is generated I want to test it against mysql_num_rows() and if there is no results clear out the users choices and start over.
You're being a little vague, but if you're merely talking about reparsing the output, you could do that using output buffering.
I'm not entirely clear what the issue is, but couldn't you decide what is to be shown before creating the HTML, and then send the right thing the first time?
To generate the query to pull the products from the data-base is very complicated because different categories have different requirements to find the correct products. so once the query is generated I want to test it against mysql_num_rows() and if there is no results clear out the users choices and start over.
In that case, just put the query inside a function that returns the result, check the row count, and if it's zero clear the filters and call that function a second time.
Output buffering (ob_start and ob_clean), combined with separating the functionality at hand into a separate file and eval()'ing that should do the trick.
Oh, and recent PHP versions actually have a goto statement... although I'll always deny mentioning anything about it. :-)
I think you're going about it a little bit off.
What you should do to reparse the page is to redirect the user to the page again, using
header('Location: thepagefile.php');
however if you actually would like to reparse the file without creating a new page, you could also just include the file again:
include thepagefile.php
But you'd probably get the same result. If you want to actually parse the output of the page you'd do something like:
ob_start(); // this is at the very top of the code/page
// all the code goes here
$output = ob_get_clean();
eval($output); // WTF?
but that actually makes no sense, but I hope it helps.
I'd actually like to know what the real problem you're trying to solve really is.
I think your looking for something like this:
<?php
ob_start(); //we start output buffering, this means nothing is send to the browser
//We do some code stuff
$time = microtime();
echo "Hai \n"; //Note taht mixing logic and output in real life
echo $time; // is terribly practice
echo "\n bai"; //I do it here purely for the example
if(/*some condition */){
$anErrorHappened = true;
}
if($anEroorHappened === true){
//Load the output in a var if you need it
//Otherwise don't
$output = ob_get_clean();
//Do other code stuff
//I.E. send an error page
include('errorPage.html');
}
else{
ob_end_flush(); //Send everything the script has echo()'d, print()'ed and send to the browser in any other way (I.E. readfile(), header() etc.)
}
?>