PHP safe for path traversal? - php

Is this php script safe for path traversal, null byte injection etc? I have tested by myself but I
don`t know sure...
<?php
if(isset($_GET['p'])) {
$allowedPages = array();
$openDir = opendir('./pages/');
while(false !== ($entry = readdir($openDir))) {
$allowedPages[] = $entry;
}
closedir($openDir);
$_GET['p'] = preg_replace('/([^.]+)(?:\.[^.]+)?$/', "$1.php", $_GET['p']);
$_GET['p'] = preg_replace('/\.[^.]+$/', '.php', $_GET['p']);
if(in_array($_GET['p'], $allowedPages)) {
include './pages/'.$_GET['p'];
} else {
header("HTTP/1.0 404 Not Found");
header("Location: ./404");
exit();
}
}
?>

Related

How do i can check if my post string contains a symbol and prevent from proceeding

Im making a simple search on my website for files in directory.
All files in it looks like example.html.
I already made that user can't search empty input and i wan't to prevent user from searching all that is in $exclude string. Otherwise if user will type in example dots, it will show every file that is in directory.
Here is my php code:
$dir = "data/pages/";
$exclude = array('.','..','.htaccess','index');
if(array_key_exists('submit', $_POST)) {
if (empty($_POST['field1'])) {
echo("<p><h3>Please fill all the fields</h3>");
}
else {
if (is_dir($dir)) {
if ($dh = opendir($dir)) {
echo("<p><h3>↓Search Results↓</h3>");
while (($file = readdir($dh)) !== false) {
$filename = pathinfo($file, PATHINFO_FILENAME);
if(preg_match("/{$_POST['field1']}/i", $filename) &&!in_array($file,$exclude)) {
echo("<p>Found: <b>$filename");
}
}
closedir($dh);
}
}
}
}
depending of the match (exact match or contain), you can do that:
if input contain some exclude:
$isOk = true;
$exclude = ['.','..','.htaccess','index'];
foreach ($exclude as $exclude){
if(str_contains($_POST['field1'], $exclude)){
$isOk = false;
}
}
if input contain some exact input
$isOk = true;
$exclude = ['.','..','.htaccess','index'];
if(in_array($_POST['field1'], $exclude, true)){
$isOk = false;
}
In your code, with exact check:
$dir = "data/pages/";
$excludes = array('.','..','.htaccess','index');
$isOk = true;
if(in_array($_POST['field1'], $excludes, true)){
$isOk = false;
}
if(array_key_exists('submit', $_POST)) {
if (empty($_POST['field1'])) {
echo("<p><h3>Please fill all the fields</h3>");
}
if (!$isOk) {
echo("<p><h3>You cannot search that!</h3>");
}
else {
if (is_dir($dir)) {
if ($dh = opendir($dir)) {
echo("<p><h3>↓Search Results↓</h3>");
while (($file = readdir($dh)) !== false) {
$filename = pathinfo($file, PATHINFO_FILENAME);
if(preg_match("/{$_POST['field1']}/i", $filename) &&!in_array($file,$exclude)) {
echo("<p>Found: <b>$filename");
}
}
closedir($dh);
}
}
}
}

read files from an array in php

I'm trying to open a directory, read just files with a .txt format and then display the contents. I've coded it out, but it doesn't do anything, although it doesn't register any errors either. Any help?
$dir = 'information';
If (is_dir($dir)) {
$handle = opendir($dir);
} else {
echo "<p>There is a system error</p>";
}
$entry=array();
while(false!==($file = readdir($handle))) {
if ( !strcmp($file, ".") || !strcmp($file, "..")) {
}
else if(substr($file, -4) == '.txt') {
$entry[] = $file;
}
foreach ($entry as $txt_file) {
if(is_file($txt_file) && is_writable($txt_file)) {
$file_open = fopen($txt_file, 'r');
while (!feof($file_open)) {
echo"<p>$file_open</p>";
}
}
}
}
Help is quite simple.
Instead
$dir = 'information';
If (is_dir($dir)) {
$handle = opendir($dir);
} else {
echo "<p>There is a system error</p>";
}
write (I am sorry for re-formatting of new lines)
$dir = 'information';
if(is_dir($dir))
{
$handle = opendir($dir);
}
else
{
echo "<p>There is a system error</p>";
}
because if has to be written only smallcaps, thus not If.
And the second part rewrite to (again, you may use your own formatting of new lines)
$entry=array();
$file = readdir($handle);
while($file !== false)
{
if(!strcmp($file, ".") || !strcmp($file, ".."))
{
}
elseif(substr($file, -4) == '.txt')
{
$entry[] = $file;
}
foreach ($entry as $txt_file)
{
if(is_file($txt_file) && is_writable($txt_file))
{
$file_open = fopen($txt_file, 'r');
while(!feof($file_open))
{
echo"<p>$file_open</p>";
}
}
}
}
because PHP has elseif, not else if like JavaScript. Also I separated $file = readdir($handle) for possible source of error.
Code part
if(!strcmp($file, ".") || !strcmp($file, ".."))
{
}
elseif(substr($file, -4) == '.txt')
{
$entry[] = $file;
}
should be shortened only to
if(substr($file, -4) == '.txt')
{
$entry[] = $file;
}
because when if part is empty, then it is not neccessary.
That is all I can do for you at this time.
Instead of iterating the directory with readdir, consider using glob() instead. It allows you to specify a pattern and it returns all files that match it.
Secondly, your while loop has an error: you conditionally add the file name to the list of files, but then you always print every file name using a foreach loop. On the first loop it will print the first file. On the second loop it will print the first and second files, etc. You should separate your while and foreach loops to fix that issue (i.e. unnest them).
Using glob, the modified code will look like:
$file_list = glob('/path/to/files/*.txt');
foreach ($file_list as $file_name) {
if (is_file($file_name) && is_writable($file_name)) {
// Do something with $file_name
}
}

Read file names from directory

I am trying to read and display all the files in a directory using this code.
It works fine for files in the same directory as the script. But when I try to display files in a folder (files/) it is giving me problems.
I've tried setting the directoy variable to many different things. like...
files/
files
/files/
etc... nothing seems to work. Does anyone have any idea why?
<?php
$dhandleFiles = opendir('files/');
$files = array();
if ($dhandleFiles) {
while (false !== ($fname = readdir($dhandleFiles))) {
if (is_file($fname) && ($fname != 'list.php') && ($fname != 'error.php') && ($fname != 'index.php')) {
$files[] = (is_dir("./$fname")) ? "{$fname}" : $fname;
}
}
closedir($dhandleFiles);
}
echo "Files";
echo "<ul>";
foreach ($files as $fname) {
echo "<li><a href='{$fname}'>{$fname}</a></li>";
}
echo "</ul>";
?>
You're not including the full path in your array:
while($fname = readdir($dhandleFiles)) {
$files[] = 'files/' . $fname;
^^^^^^^^---must include actual path
}
Remember that readdir() returns ONLY the filename, without path information.
This should help - take a look at SplFileInfo too.
<?php
class ExcludedFilesFilter extends FilterIterator {
protected
$excluded = array(
'list.php',
'error.php',
'index.php',
);
public function accept() {
$isFile = $this->current()->isFile();
$isExcluded = in_array($this->current(), $this->excluded);
return $isFile && ! $isExcluded;
}
}
$dir = new DirectoryIterator(realpath('.'));
foreach (new ExcludedFilesFilter($dir) as $file) {
printf("%s\n", $file->getRealpath());
}
How about using glob function.
<?php
define('MYBASEPATH' , 'files/');
foreach (glob(MYBASEPATH . '*.php') as $fname) {
if($fname != 'list.php' && $fname != 'error.php' && $fname != 'index.php') {
$files[] = $fname;
}
}
?>
read more about getting all files in directory here
This reads and prints filenames from a sub-directory:
$d = dir("myfiles");
while (false !== ($entry = $d->read())) {
if ($entry != ".") {
if ($entry != "..") {
print"$entry";
}
}
}
$d->close();

FTP_DELETE not working?

Hey guys I have my script here that is supposed to do some stuff then delete a file, unfortunetly my files never unlink. I"m wondering what the reason for this might be? Permissions was the only thing I could think of, or maybe the output buffer is messing up? I really don't know, but would appreciate some advice on how to handle it. Issue in question is that last IF() block.
public function remoteFtp() {
$enabled = Mage::getStoreConfig('cataloginventory/settings/use_ftp');
$remove = Mage::getStoreConfig('cataloginventory/settings/ftp_remove_file');
if ($enabled == 0) {
return true;
}
$base_path = Mage::getBaseDir('base');
$ftp_url = Mage::getStoreConfig('cataloginventory/settings/ftp_url');
$ftp_user = Mage::getStoreConfig('cataloginventory/settings/ftp_user');
$ftp_pass = Mage::getStoreConfig('cataloginventory/settings/ftp_password');
$ftp_remote_dir = Mage::getStoreConfig('cataloginventory/settings/ftp_remote_dir');
$ftp_filename_filter = Mage::getStoreConfig('cataloginventory/settings/ftp_remote_filename');
$ftp_file = $base_path . '/edi/working/working.edi';
$handle = fopen($ftp_file, 'w');
$conn_id = ftp_connect($ftp_url);
ftp_login($conn_id, $ftp_user, $ftp_pass) or die("unable to login");
if ($ftp_remote_dir) {
ftp_chdir($conn_id, $ftp_remote_dir);
}
//is there a file
$remote_list = ftp_nlist($conn_id, ".");
$exists = count($remote_list);
if ($exists > 0) {
$len = strlen($ftp_filename_filter) - 1;
foreach ($remote_list as $name) {
if (substr($ftp_filename_filter, 0, 1) == "*") {
if (substr($name, '-' . $len) == substr($ftp_filename_filter, '-' . $len)) {
$ftp_remote_name = $name;
}
}
if (substr($ftp_filename_filter, strlen($name) - 1) == "*") {
if (substr($ftp_filename_filter, 0, $len) == substr($name, 0, $len)) {
$ftp_remote_name = $name;
}
}
if ($ftp_filename_filter == $name) {
$ftp_remote_name = $name;
}
}
}
if (ftp_fget($conn_id, $handle, $ftp_remote_name, FTP_ASCII, 0)) {
echo "successfully written to $ftp_file <br />";
if ($remove == 1) {
ftp_delete($conn_id, $ftp_remote_name);
}
} else {
echo "There was a problem while downloading $ftp_remote_name to $ftp_file <br />";
}
ftp_close($conn_id);
}
The answer was that the system variable $remove = Mage::getStoreConfig('cataloginventory/settings/ftp_remove_file'); was set to BOOL(false)

PHP Recursive Directory Handle Problem

getSettings() seems to only read and output 1 settings.php file in the directory. How do I get it to read and output all the settings.php file contents?
<?php
$config = array("os"=>"windows","directory"=>"../Core-Stack/Themes/","ignore"=>array('_vti_cnf','cgi-bin','.','..'));
function getSettings($dir, $issubdir = false) {
global $config, $SETTINGS;
if ($config['os'] == "unix")
$delimiter = "/";
else if ($config['os'] == "windows")
$delimiter = "\\";
if (!file_exists($dir) || !is_dir($dir) || !is_readable($dir)) {
echo "Error: \"$dir\" is not a directory, or I cannot read it properly.";
return 0;
} else if ($od = opendir($dir)) {
while (($file = readdir($od)) !== false) {
if (!in_array($file, $config['ignore'])) {
$path = $dir . $delimiter . $file;
if (is_dir($path))
getSettings($path, true);
elseif (is_file($path) && $file == "settings.php")
include ($path);
}
}
closedir($od);
}
}
getSettings($config['directory'], true);
echo "Theme Name: ";
echo $SETTINGS['theme_name'];
echo "<br>";
echo "Theme Creator: ";
echo $SETTINGS['theme_creator'];
echo "<br>";
echo "Theme Version: ";
echo $SETTINGS['theme_version'];
echo "<br>";
echo "Theme Creation Date: ";
echo $SETTINGS['theme_creation_date'];
?>
You should store directory contents before recursion, your else-if block should be like this:
else if ($od = opendir($dir)) {
$subdirs = array();
while (($file = readdir($od)) !== false) {
if (!in_array($file, $config['ignore'])) {
$path = $dir . $delimiter . $file;
if (is_dir($path)) $subdirs[] = $path;
elseif (is_file($path) && $file == "settings.php") include ($path);
}
}
closedir($od);
foreach($subdirs as $subdir)
getSettings($subdir, true);
}
Most likely your "settings" files are defining settings somehow like $SETTINGS = array(...); and of course that way you will only see contents from the latest included file. What you could do here without remaking the whole thing would be either:
without changing settings.php:
//...
elseif (is_file($path) && $file == "settings.php") {
$OLD_SETTINGS = $SETTINGS;
include ($path);
$SETTINGS = array_merge($OLD_SETTINGS, $SETTINGS);
}
//...
or if you can change the settings.php files:
//...
elseif (is_file($path) && $file == "settings.php") {
$SETTINGS = array_merge($SETTINGS, include ($path));
}
//...
//----in settings.php
return array(
'option' => 'foobar',
//...
);
That's of course if I got your intensions right. If not - then please edit your question and add more details.
UPDATE
also you could use scandir to fit the function in less lines and prevent potential problems with heap if the tree is VERY deep, like this:
function getSettings($dir, $issubdir = false) {
global $config, $SETTINGS;
if (!file_exists($dir) || !is_dir($dir) || !is_readable($dir)) {
echo "Error: \"$dir\" is not a directory, or I cannot read it properly.";
return 0;
} else if ($files = scandir($dir)) {
foreach ($files as $file) {
if (in_array($file, $config['ignore'])) continue;
$path = $dir . DIRECTORY_SEPARATOR . $file;
if (is_dir($path))
getSettings($path, true);
elseif (is_file($path) && $file == "settings.php")
$SETTINGS = array_merge($SETTINGS, include ($path));
}
}
}

Categories