Search code examples
phpdirectoryscandir

How to link and browse sub directories using php


I want to make a simple file browser using PHP. As I am new here in PHP. I need some push,

<table class="table">
  <thead>
    <tr>
      <th scope="col">Directory Name</th>
      <th scope="col">Last Modified</th>
      <th scope="col">Size</th>
    </tr>
  </thead>
  <tbody>
  <?php
        $folders= new DirectoryIterator(__DIR__);
        while($folders->valid()){

    ?>
    <tr>

      <td><?php echo "<a href='{$folders->current()}'>{$folders->current()}</a>" ?></td>
      <td></td>
      <td><?php echo $folders->getSize();?></td>
    </tr>

    <?php

        $folders->next();
    } ?>
  </tbody>
</table>

I have come to this so far. How can I complete that code in order to have a functional file browser using php. Thank you.


Solution

  • Here's a method to do that which also employs a level of security. While this may just be a school project or tutorial, you should always consider security. I have commented below but basically here is what's happening.

    You first set a $rootDirectory (which you will have to modify - this one is set to my filesystem). The rootDirectory is as far as you'll allow the browser to go - because as you see, the little .. link at the top of each folder/file list which allows for upward directory traversal and anyone could navigate themselves into stuff you don't want to ever expose.

    The links to the listed folders are modified to pass along the path to that folder FROM the root directory. That way no one could fake a link to some inner OS folder. The links pass along this path in a GET variable (?dir=/some/path/). When the page loads, it looks for that GET variable, adds on the root directory to the beginning and puts it through a couple checks to make sure it's a real directory, that it isn't outside of our rootDirectory, etc. We do this by first converting it with realpath(). The incoming dir variable could be /myRootDirectory/../../../etc/mySecretConfigFile.conf which would pass our check because it has the root directory in it. realpath() removes all those ../ so we can work with it.

     <table class="table">
      <thead>
        <tr>
          <th scope="col">Directory Name</th>
          <th scope="col">Last Modified</th>
          <th scope="col">Size</th>
        </tr>
      </thead>
      <tbody>
      <?php
            // simple security - do not allow the file browser to get out of this root directory
            // SET THIS FOR YOUR SYSTEM
            $rootDirectory = '/Users/laphona/Sites/htdocs';
    
            // $dir = __DIR__ ;
    
            if ($_GET && $_GET['dir']) $dir = $rootDirectory . $_GET['dir'];
            else $dir = $rootDirectory;
            $dir = realpath($dir) ; // this takes out the ../ and the ./ and the .. that makes upward traversal possible despite our checks
           
            // if our root directory isn't present in the $dir
            if (strpos($dir, $rootDirectory) === false ) $dir = $rootDirectory ;
            // if our root directory isn't the very first part of the $dir
            if (strpos($dir, $rootDirectory) !== 0 ) $dir = $rootDirectory ;
            $folders= new DirectoryIterator($dir);
            while($folders->valid()){
                // set up the path here to be the direct path to the folder, minus our root directory
                $myPath = str_replace($rootDirectory, '', $folders->getPath()) . "/" . $folders->current();
                $myItem = "<a href='".$_SERVER['PHP_SELF']."?dir={$myPath}'>{$folders->current()}</a>" ;
                // if it's a file just present the name, no link
                if ($folders->isFile()) $myItem = $folders->current();
        ?>
        <tr>
    
          <td><?php echo $myItem ?></td>
          <td></td>
          <td><?php echo $folders->getSize();?></td>
        </tr>
    
        <?php
    
            $folders->next();
        } ?>
      </tbody>
    </table>