0

Within a project, I have the ability to create directories and files as well as edit those files. But now I'm adding a delete functionality to delete directories and all contents within. I did some research, and found a stack overflow answer from 2009 that had two options. I am curious as to which one is better and if there have been any updates to security of the two (just making sure I'm using a safe method). Below is a copy and paste of the answer.

Stack Overflow Answer:

This function will allow you to delete any folder (as long as it's writable) and it's files and subdirectories.

function Delete($path)
{
    if (is_dir($path) === true)
    {
        $files = array_diff(scandir($path), array('.', '..'));

        foreach ($files as $file)
        {
            Delete(realpath($path) . '/' . $file);
        }

        return rmdir($path);
    }

else if (is_file($path) === true)
{
    return unlink($path);
}

return false;
}

Or without recursion using RecursiveDirectoryIterator:

function Delete($path)
{
    if (is_dir($path) === true)
    {
        $files = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($path), RecursiveIteratorIterator::CHILD_FIRST);

        foreach ($files as $file)
        {
            if (in_array($file->getBasename(), array('.', '..')) !== true)
            {
                if ($file->isDir() === true)
                {
                    rmdir($file->getPathName());
                }

                else if (($file->isFile() === true) || ($file->isLink() === true))
                {
                    unlink($file->getPathname());
                }
            }
        }

        return rmdir($path);
    }

    else if ((is_file($path) === true) || (is_link($path) === true))
    {
        return unlink($path);
    }

    return false;
    } 
Community
  • 1
  • 1
kdjernigan
  • 417
  • 2
  • 7
  • 22
  • 1
    I don't see what you are meaning by *secure*. Both will cause damage if you do not know what you are doing. – kittycat Jan 15 '14 at 19:19
  • secure as in vulnerabilities in code or known unsafe methods. I know there are certain things that are not recommended because of known vulnerabilities, and just wanted to make sure that these didn't contain any. – kdjernigan Jan 15 '14 at 19:21
  • ^ so what you are asking is a code review? if yes, then this might be OT – reikyoushin Jan 15 '14 at 19:25
  • I know the code was posted in 2009 and quite a bit has changed since 2009 so I just wanted to make sure it is a safe code to use and which method is better. There are two different methods above. I mean, if there's a better way than those, then I'm open to those too. – kdjernigan Jan 15 '14 at 19:26
  • @kdjernigan you should probably understand what each script is doing so you know what the differences are before you ask about which is safe. Look up the functions in the PHP manual. – kittycat Jan 15 '14 at 19:32

2 Answers2

2

You can also use underlying OS commands to do this:

if(is_dir($path)) {
    $path_escaped = escapeshellcmd(realpath($path));
    $output = shell_exec('rm -fR ' . $path_escaped);
}

It should be noted that you would definitely want to be able to verify that the `$path value provided (if input from untrusted source) falls within whatever directory or set of directories that you want to allow such functionality in.

For example, you might do something like:

$allowable_delete_dirs = array(
    '/var/www/some_dir/',
    '/var/www/some_other_dir/'
);

if(is_dir($path)) {
    $real_path = realpath($path);
    $result_array = array_filter($allowable_delete_dirs, function($value) use ($real_path) {
        return (str_pos($real_path, $value) === 0 && $real_path !== $value);
    }
    if (count($result_array) > 0) {
        // the path provided matches one or more of the allowable directories
        $output = shell_exec('rm -fR ' . escapeshellcmd($real_path));
    }
}
Mike Brant
  • 70,514
  • 10
  • 99
  • 103
  • 2
    So what happens if `$path` happens to be `/` ? – kittycat Jan 15 '14 at 19:22
  • @crypticツ Well likely, unless running as root, PHP won't have the ability to execute. Seriously though, one would probably want to limit the scope of directories that could be deleted in such a manner, but that really isn't covered in the examples in original question either. To me this might be achieved by making sure the path as is going to used falls within a known directory structure. I will add commentary to the answer. – Mike Brant Jan 15 '14 at 19:25
  • How would I make sure the path falls within a certain directory structure? – kdjernigan Jan 15 '14 at 19:32
  • Let's hope server is not badly configured as most are and is not running as root. But also you need to think of this `../` will be canonicalized to `/path_to/webroot/parent_folder_of_script` which I'm sure PHP will have permissions to access. Edit: just saw your update, much better method. – kittycat Jan 15 '14 at 19:33
  • 1
    @kdjernigan I updated answer to give an example of how you might restrict the delete to certain directory hierarchies. Note this example would not let you delete the directories actually specified, but rather those underneath those directories. – Mike Brant Jan 15 '14 at 19:34
  • @cryptic ツ - the server is a cPanel server and the website is under an individual account inside cPanel, not root. – kdjernigan Jan 15 '14 at 19:37
  • 2
    @kdjernigan but still any folder which that user owns can be deleted by PHP. Mike Brant's answer creates a whitelist to prevent that, but never run such function unless you do that or hardcode the path as you could end up having whole webroot deleted. – kittycat Jan 15 '14 at 19:39
  • @cryptic ツ - oh definitely. Everything will be hardcoded to a specific location, but just in case I make this into a function that is open, I definitely like the whitelist concept. I will be using this in a couple of spots, which is why I may make this into a function. But everything I delete is within certain folders that are all organized, so the whitelist may be the best option – kdjernigan Jan 15 '14 at 19:42
  • Just to check, can I use `$_SERVER['DOCUMENT_ROOT'] .'/directory1/', $_SERVER['DOCUMENT_ROOT'] .'/directory2/'` in the array to define the whitelist? – kdjernigan Jan 15 '14 at 19:46
  • 1
    @kdjernigan That should be fine unless there is some reason that `$_SERVER['DOCUMENT_ROOT']` value might be tampered with in your code. – Mike Brant Jan 15 '14 at 19:57
0

Use this methods for deleting any directory or file,even if directory has sub directories :

<?php

function list_files_dirs($dir) {
    if (!is_dir($dir))
        return ('Directory ' . $dir . ' doesn\'t exists.');
    $files = scandir($dir);
    unset($files[array_search('.', $files)]);
    unset($files[array_search('..', $files)]);
    return $files;
}

function delete_files($dir) {
    if (substr($dir, -1) == '/')
        $dir = substr($dir, 0, strlen($dir) - 1);
    if (!is_dir($dir) && !file_exists($dir))
        return $dir . ' doesn\'t exists.';

    function rmdir_recursive($dir, $instance) {
        foreach (list_files_dirs($dir) as $file) {
            if (is_dir("$dir/$file"))
                rmdir_recursive($dir . "/" . $file, $instance);
            else
                unlink($dir . "/" . $file);
        }
        rmdir($dir);
    }

    rmdir_recursive($dir, $this);
    return true;
}
echo delete_files("directory/or/file");
?>
Alireza Fallah
  • 4,609
  • 3
  • 31
  • 57
  • would this be hard to modify to restrict the function to a specific directory? Such as, it can only delete files/folders within `$_SERVER['DOCUMENT_ROOT'] .'/directory/'` folder. – kdjernigan Jan 15 '14 at 19:40
  • 1
    simply put a `if` in start and check if directory is not allowed, then `die('not allowed')` – Alireza Fallah Jan 15 '14 at 19:47