0

What is the best way to include a file whose folder and file name are obtained from the query string?

In myServer.com/data there are hundreds of folders with several files in each. These are plain html files.

In myServer.com/code is a php targetPage.php

I can build an URL like this:

http://www.myServer.com/code/targetPage.php?folder=folder132&name=file23

What is the php to read the source file within the source folder and include the content of a source "div" as innerHTML of the "targetDiv" in the target page?

<body><div id="targetDiv">This is the place holde div</div>

I'm rather new to php, hence the question.

Didier Levy
  • 3,393
  • 9
  • 35
  • 57

3 Answers3

1

This is basically an invitation to potential hackers, who no doubt will appreciate you giving them the ability to easily specify which files to include into (and therefore execute) in your PHP page.

At first blush, this bears at least a passing resemblance to the sort of issue that leads to SQL Injection attacks. Are you really sure you want to take the path to your include files from query strings? The query string is exposed to the end user. One issue I see is that this would tend to expose the internal structure of your site to visitors. Another issue is that I, as a visitor, would be able to experiment with different include files until I, presumably, find one that breaks the site in some way (at best) or (at worst) compromises your security and gives me greater access to your server or your database, which would then let me dig deeper into your system until I have it totally compromised, or am able to delete or update things in your database that you don't want me to. By "I" and "me," of course I hypothetically mean somebody trying to hack your site.

I would explore finding a different (server-side, not exposed to visitors) way to figure out which file to include.

Craig Tullis
  • 9,939
  • 2
  • 21
  • 21
  • 1
    How do you do SQL injection without SQL ? – Bart Friederichs Apr 17 '13 at 06:37
  • Well, in this case it would be PHP injection, yeah? Same basic concept, though. :-) – Craig Tullis Apr 17 '13 at 06:38
  • I think there is no danger because the query string won't be that explicit: It will contain something like "?paramA=123?paramB=12" without being an explicit path. With "123" I will setup the folder path and with paramB the file name. – Didier Levy Apr 17 '13 at 06:49
  • @Dieder, that seems safer, but if those id values correspond directly to actual directory and file names, I can still manipulate the URL for the same effect. If, on the other hand, they are surrogate id values that the server can map to the real directories, I think you'd be in a lot better shape. By "same basic concept," in reference to SQL Injection, if I can pass a reference to a potentially executable resource in the query string, it really doesn't matter whether it is SQL, or PHP, or Javascript. Is every single one of these includable files going to be fully security-vetted and certified? – Craig Tullis Apr 17 '13 at 06:52
  • All the files-to-include have been created by a software of mine as plain html files. No user can add his own files. – Didier Levy Apr 17 '13 at 06:58
  • I suppose in that sense, your files are very similar to database resources that you might request by id, and it's good that they're just HTML, and not PHP files. I might add HTML encoding to @Bart's answer, just to be extra safe. If you did that and wanted to have JavaScript in those files, you could include the JavaScript using *.js files. Now I'm just thinking out-loud; don't want to beat this to death. :-) – Craig Tullis Apr 17 '13 at 07:06
  • Although, I suppose that HTML Encoding the files (http://stackoverflow.com/a/1873809/618649) would just display the entire HTML structure in your page, which I'm sure isn't your goal. So... never mind that. I think it's bed time. :-) – Craig Tullis Apr 17 '13 at 07:11
1

First, make absolutely sure that the file included is allowed, preferably by using a whitelist.

Then, you can use a function like file_get_contents() to read the file into a string and output it with echo.

To wrap up (this is a way to do it, other ways exist):

$file = $_GET['folder']."/".$_GET['file'];

if (in_array($file, $whitelist)) {
    $contents = file_get_contents($file);
} else {
    $contents = "Not allowed";
}

echo "<body><div id='targetDiv'>$contents</div>"

You could for example generate the whitelist by scanning the directory you want to be able to show. (Which is outside the scope of this answer ;-)).

Another solution could be to check the canonical resulting path with realpath().

Bart Friederichs
  • 33,050
  • 15
  • 95
  • 195
  • I'm not saying it's impossible to make this work with a degree of security, but I'd bet there are ways to accomplish the same thing within the structure that is already built, by passing some sort of id in the request which maps to the desired file, and then PHP code completely un-exposed to the visitor would translate that into the appropriate file reference. – Craig Tullis Apr 17 '13 at 06:45
  • @Craig - Exactement, mon ami :-) – Didier Levy Apr 17 '13 at 06:51
  • @Craig I agree, but poster states he has hundreds of folders with several files, probably generated by some other program. Generating IDs means having another task, more code, hence more bugs. What I am proposing is basically to use the folder and file name as their IDs and map them to a file. – Bart Friederichs Apr 17 '13 at 06:52
  • @Bart--agreed, the whitelist does touch on that, for sure. I'm just sort of raising the general alarm, I guess. :-) – Craig Tullis Apr 17 '13 at 06:56
0

If the folder should be just a name without directory separators you can use basename() to strip off the separators from both folder and name. The folder requires special treatment because .. would be technically valid, but you don't want to allow going outside of your base directory.

if (isset($_GET['folder'], $_GET['name'])) {
    $folder = trim(basename($_GET['folder']), '.') ?: '.';
    $name = basename($_GET['name']);

    $file = BASE_DIR . "/$folder/$name";
    if (is_file($file) && is_readable($file)) {
        echo file_get_contents($file);
    }
}

BASE_DIR would be a constant that contains the base directory from which to load the files.

Ja͢ck
  • 170,779
  • 38
  • 263
  • 309