Preg_replace do not exist in php7. What can i do? [duplicate] - php

This question already has answers here:
Replace preg_replace() e modifier with preg_replace_callback
(3 answers)
Closed 2 years ago.
I have a form where i get a filename from a inputbox. I create the a directory and change the extension form "gpx" to "xml" of the file before i upload the file to my directory.
In php5 i need preg_replace, but in php i can't do it anymore.
I have this code:
My old code is:
if (!file_exists($dirPath)) {
mkdir($dirPath, 0755,true);
}
$target = '/'.$mappe.'/';
$target = $dirPath .'/'. basename( $_FILES['gpsfilnavn']['name']);
$target = preg_replace("/(\w+).gpx/ie","$1.'.xml'",$target);
$xmlfil = $xmlfil . basename( $_FILES['gpsfilnavn']['name']);
$xmlfil = preg_replace("/(\w+).gpx/ie","$1.'.xml'",$xmlfil);
if(move_uploaded_file($_FILES['gpsfilnavn']['tmp_name'], $target)) {
echo "The file ". basename( $_FILES['gpsfilnavn']['name'])." has been uploaded";
Can anybody help me what i have to change?

There has been a change since PHP 7 regarding the preg_replace() function.
According to the man-page
7.0.0 Support for the /e modifier has been removed. Use preg_replace_callback() instead.
Maybe this helps you?

I've stumbled upon this issue today when upgrading a phpBB-based website from PHP5 to PHP7. I came up with three different workarounds that can be used for different scenarios, the second one being the only viable one for mine since I had template-based regexp stored within the filesystem/db instead of static ones, which I couldn't easily alter.
Basically, I went from this:
$input = preg_replace($search, $replace, $input);
to something like this:
$input = preg_replace($search,
function($m) use ($replace) {
$rep = $replace;
for ($i = 1; $i<count($m); $i++) {
$rep = str_replace('\\'.$i, '$m['.$i.']', $rep);
$rep = str_replace('\$'.$i, '$m['.$i.']', $rep);
}
eval('$str='.$rep);
return $str;
},
$input);
Needless to say, this is nothing more than a quick-and-dirty workaround, only "viable" for those who cannot easily refactor their code with the updated preg_replace_callback function without having to use any potentially unsecure eval() call: for this very reason it should only be used if the developer has full control over the replacement strings/rules and is able to properly test/debug them. That said, in my specific scenario, I was able to effectively use that in order to get the job done.
IMPORTANT: the str_replace and/or the eval() could break some replacement rules unless they are "properly" defined taking the above code into account.
For further info regarding this issue and other alternative workarounds you can also read this post on my blog.

Related

Is it possible to escape this str_replace() for path traversal?

I'm testing one of our PHP web applications for security issues.
The system the code is running on runs with at least PHP7.2.
Now I found something like the following in the code (simplified for this question, but boils down to this):
$file = $_GET['file'];
$path = "/some/directory/" . $file;
$path = str_replace(['../', '..'], '', $path);
echo file_get_contents($path);
Is it possible to modify the file parameter in a way that we can escape /some/directory, so that after the str_replace() the file_get_contents()-call looks something like: file_get_contents(/some/directory/../../etc/passwd)?
Edit:
I can't change the order of code execution. I can only define the value of $_GET['file'] with my request.
Furthermore I know how to make this more secure but for my research I intend to break it.
Basically what needs to be done is somehow tricking out the str_replace() into leaving some ../ behind.
I tried for a few hours now, with various approaches, but - luckily for our application - couldn't get it working.
Do you have any ideas?
You can fiddle around with the code here: https://3v4l.org/3ehYA

Is it possible to change the behavior of PHP's print_r function [duplicate]

This question already has answers here:
making print_r use PHP_EOL
(5 answers)
Closed 6 years ago.
I've been coding in PHP for a long time (15+ years now), and I usually do so on a Windows OS, though most of the time it's for execution on Linux servers. Over the years I've run up against an annoyance that, while not important, has proved to be a bit irritating, and I've gotten to the point where I want to see if I can address it somehow. Here's the problem:
When coding, I often find it useful to output the contents of an array to a text file so that I can view it's contents. For example:
$fileArray = file('path/to/file');
$faString = print_r($fileArray, true);
$save = file_put_contents('fileArray.txt', $faString);
Now when I open the file fileArray.txt in Notepad, the contents of the file are all displayed on a single line, rather than the nice, pretty structure seen if the file were opened in Wordpad. This is because, regardless of OS, PHP's print_r function uses \n for newlines, rather than \r\n. I can certainly perform such replacement myself by simply adding just one line of code to make the necessary replacements, ans therein lies the problem. That one, single line of extra code translates back through my years into literally hundreds of extra steps that should not be necessary. I'm a lazy coder, and this has become unacceptable.
Currently, on my dev machine, I've got a different sort of work-around in place (shown below), but this has it's own set of problems, so I'd like to find a way to "coerce" PHP into putting in the "proper" newline characters without all that extra code. I doubt that this is likely to be possible, but I'll never find out if I never ask, so...
Anyway, my current work-around goes like this. I have, in my PHP include path, a file (print_w.php) which includes the following code:
<?php
function print_w($in, $saveToString = false) {
$out = print_r($in, true);
$out = str_replace("\n", "\r\n", $out);
switch ($saveToString) {
case true: return $out;
default: echo $out;
}
}
?>
I also have auto_prepend_file set to this same file in php.ini, so that it automatically includes it every time PHP executes a script on my dev machine. I then use the function print_w instead of print_r while testing my scripts. This works well, so long as when I upload a script to a remote server I make sure that all references to the function print_w are removed or commented out. If I miss one, I (of course) get a fatal error, which can prove more frustrating than the original problem, but I make it a point to carefully proofread my code prior to uploading, so it's not often an issue.
So after all that rambling, my question is, Is there a way to change the behavior of print_r (or similar PHP functions) to use Windows newlines, rather than Linux newlines on a Windows machine?
Thanks for your time.
Ok, after further research, I've found a better work-around that suite my needs, and eliminates the need to call a custom function instead of print_r. This new work-around goes like this:
I still have to have an included file (I've kept the same name so as not to have to mess with php.ini), and php.ini still has the auto_prepend_file setting in place, but the code in print_w.php is changes a bit:
<?php
rename_function('print_r', 'print_rw');
function print_r($in, $saveToString = false) {
$out = print_rw($in, true);
$out = str_replace("\n", "\r\n", $out);
switch ($saveToString) {
case true: return $out;
default: echo $out;
}
}
?>
This effectively alters the behavior of the print_r function on my local machine, without my having to call custom functions, and having to make sure that all references to that custom function are neutralized. By using PHP's rename_function I was able to effectively rewrite how print_r behaves, making it possible to address my problem.

How to get extensions for files with data in paths?

I'm writing an image upload script and have whitelisted common image extensions. I need to be able to get the extension of files to determine if they are in the whitelist, but I'm having trouble with some URLs.
Here is an example:
http://pisces.bbystatic.com/image/BestBuy_US/images/products/5512/5512124_ra.jpg;canvasHeight=280;canvasWidth=280
My code:
echo pathinfo($url, PATHINFO_EXTENSION);
Outputs the following:
jpg;canvasHeight=280;canvasWidth=280
DEMO
I could change my code to this, which fixes the problem:
$parts = explode(';', pathinfo($url, PATHINFO_EXTENSION));
echo $parts[0];
DEMO
However, I don't know if that is the best approach to take here (for one thing, because other websites might use different delimiters for data instead of a semicolon);
Is there a better way?
EDIT: It might be worth noting that I tried uploading the image here as a test (using the example URL) and StackOverflow had no trouble uploading it.
Using pathinfo() only will match cases like http://google.com -> com. Use parse_url first to safely get the URL path, then use pathinfo to get the file extension.
If the URL uses query strings (e.g. ?foo=bar), that code should be enough. But given your example wherein the URL uses some custom format instead of query strings, you can use RegEx to select only the first alphanumeric part of $ext.
$url = 'http://pisces.bbystatic.com/image/BestBuy_US/images/products/5512/5512124_ra.jpg;canvasHeight=280;canvasWidth=280';
$ext = pathinfo(parse_url($url, PHP_URL_PATH), PATHINFO_EXTENSION);
if (!empty($ext)) {
preg_match('/[a-zA-Z0-9]+/', $ext, $matches);
$ext = $matches[0];
}
echo $ext;

how to safely join strings to a path in php? [duplicate]

This question already has answers here:
Preventing Directory Traversal in PHP but allowing paths
(7 answers)
Closed 9 years ago.
I have a constant beginning of a string, and a variable ending, how can I secure the string so that is doesn't create a step-back (or step-up) in case the string I inject contains
../
Here is a short sample code:
$dir = 'my/base/path/';
$file = $dir . $userSelectedFilename;
unlink($file);
If $userSelectedFilename would be '../../myFileName' I assume that would cause my script to actually try to unlink something two directory levels up my/myFilename which is clearly not something I want to allow, I want to keep it under the basepath my/base/path/ under all circumstances.
I suggest the the following, and only the following method:
<?
$dir = 'my/base/path/';
$file = $dir . $userSelectedFilename;
if(strpos(realpath($file),realpath($dir)) === 0) && is_file($file)) { // beware of the three ===
unlink($file);
}
Why?
It is safe to rely on realpath to find out the real location of a file which eliminates directory traversal / multibyte double-dots etc.
After that we check whether the beginning of the reapath of the file is really the beginning of our expacted directory (strpos).
After that we also check whether the file is really a file and not some symlink pointing elswhere or something like that.
I have seen character eliminating solutions been broken by multibyte strings and similar attacks.
This method so far far withstands all of these.
You could filter out those characters by doing something like:
$file = preg_match("/\.\.\//", "", $file)
which will remove occurrences of the string ../
And just a side note, you should probably find a different way of allowing users to select files to delete rather than allowing them to input the path as a string, maybe by showing them a directory listing of files they can delete or something like that.
You can do this "my/base/path/../../dir/", if you want "real" path use this :
echo realpath($dir."../../dir/"); // my/dir
http://php.net/manual/en/function.realpath.php
Using regex to validate the string for ../ or /../ and not accepting the string if the regex returns true:
function validatePath($path) {
if(preg_match('#\/\.\.\/#',$path))
return False;
}

Am I using preg_replace correctly (PHP)?

I think I have this right but I would like someone to verify that.
function storageAmmount()
{
system('stat /web/'.$user."/ | grep Size: > /web/".$user."/log/storage");
$storage = fopen('/web/'.$user.'/log/storage');
$storage = str_replace('Size:', " ", $storage);
$storage = preg_replace('Blocks:(((A-Z), a-z), 1-9)','',$storage);
}
This is the line in the text file:
Size: 4096 Blocks: 8 IO Block: 4096 directory
I am trying to get just the numeric value the proceeds "Size: ", the word Size: and everything else is usless to me.
I am mainly looking at the preg_replace. Is it just me or is regex a tad bit confusing? Any thoughts. Thanks for any help in advance.
Cheers!,
Phill
Ok,
Here is what the function looks like now:
function storageAmmount()
{
$storage = filesize('/web/'.$user.'/');
$final = $storage/(1024*1024*1024);
return $final;
}
Where would I put the number_format(), I am not really sure if it would go in the equation or in the return statement. I have tred it in both an all it returns is "0.00".
V1.
function storageAmmount()
{
$storage = filesize('/web/'.$user.'/');
$final = number_format($storage/(1024*1024*1024), 2);
return $final;
}
or V2.
function storageAmmount()
{
$storage = filesize('/web/'.$user.'/');
$final = $storage/(1024*1024*1024);
return number_format($final, 2);
}
neither work and they both return "0.00". Any thoughts?
Looks like you are trying to get the size of the file in bytes. If so why not just use the filesize function of PHP which takes the file name as its argument and returns the size of the file in bytes.
function storageAmmount(){
$storage = filesize('/web/'.$user);
}
No, you're not using preg_replace properly.
There's a lot of misunderstanding in your code; to correct it would mean I'd have to explain the basics of how Regex works. I really recommend reading a few primers on the subject. There's a good one here: http://www.regular-expressions.info/
In fact, what you're trying to do here with the str_replace and the preg_replace together would be better achieved using a single preg_match.
Something like this would do the trick:
$matches = preg_match('/Size: (\d+)/',$storage);
$storage = $matches[1];
The (\d+) picks up any number of digits and puts them into an element in the $matches array. Putting Size: in front of that forces it to only recognise the digits that are immediately after Size: in your input string.
If your input string is consistently formatted in the way you described, you could also do it without using any preg_ functions at all; just explode() on a space character and pick up the second element. No regex required.
The best usage of regex is
// preg_match solution
$storage = 'Size: 4096 Blocks: 8 IO Block: 4096 directory';
if (preg_match('/Size: (?P<size>\d+)/', $storage, $matches)) {
return matches['size'];
}
But if you are doing it localy, you can use th php function stat
// php stat solution
$f = escapeshellcmd($user);
if ($stat = #stat('/web/'.$f.'/log/storage')) {
return $stat['size'];
}
Bearing in mind the fact that you're already using string manipulation (don't quite get why - a single regular expression could handle it all), I don't know why you don't proceed down this path.
For example using explode:
function storageAmount($user) {
system(escapeshellcmd('stat /web/'.$user.'/ | grep Size: > /web/'.$user.'/log/storage'));
$storageChunks = explode(' ', file_get_contents('/web/'.$user.'/log/storage'));
return $storageChunks[1];
}
Incidentally:
The $user variable doesn't exist within the scope of your function - you either need to pass it in as an argument as I've done, or make it a global.
You should really use escapeshellcmd on all commands passed to system/exec, etc.
You're using fopen incorrectly. fopen returns a resource which you then need to read from via fread, etc. prior to using fclose. That said, I've replaced this with file_get_contents which does the open/read/close for you.
I'm not really sure what you're attempting to do with the system command, but I've left it as-is. However, you could just get the result of the grep back directly as the last string output rather than having to output it to a file. (This is what the system command returns.) You're also mixing ' and " as string delimiters - this won't work. (Just use one consistently.)
I suspect you actually want to the final line of "df --si /web/'.$user.'/'" command as otherwise you'll always be returning the value 4096.
have you tried
$storage = preg_replace('Block:.*','',$storage)
?
Even better would be to use
function storageAmmount()
{
exec('stat /web/'.$user.'/ | grep Size:',$output);
preg_match("/Size: ([0-9]*).*/",$output[0],$matches);
return $matches[1];
}
(tested on my machine)

Categories