logo

March 27th, 2007

Recently I wrote an AJAX-based file explorer which uses an underlying XML document for all of its semantic structure. The idea is that a tree structure is served via PHP (in my case) or some other mechanism (in some other case) and then represented as a navigable directory structure in a browser.

The request is made from the client in javascript with an XMLHttpRequest object. One thing I didn’t know first-off was that different browsers have different permission shcemes for XMLHttpRequest - but the one thing they all have in common is that a request is always allowed if it is made within the current domain.

For instance, a request to http://www.fiddlerelf.com/ is OK from here, but to http://www.google.com is not.

To get around this, I used a proxy. A proxy server is basically a middle-man whose sole purpose is to process your request as if it were his own, and return the response to you as if he fulfilled it. If you put the proxy in your domain your XMLHttpRequest object will always have access to it, and from the proxy you have access beyond your domain.

Below is a very simple example of how this can be done in PHP. Assume this is the body of a file called “fetch.php”.

$url = $_GET['url'];
$file = fopen ($url, “r”);
if (!$file)
{
echo “Error.\n”;
}
else
{
header(’Content-Type: text/xml’);
while (!feof ($file))
{
$line = fgets ($file, 1024);
echo $line;
}
}
fclose($file);

A request to this service can then be made this way:

http://www.yourdomain.com/fetch.php?url=http://www.somewhere.com

I’ve chosen the “url” as the query string variable just arbitrarily (though obviously it makes sense).

The first thing to do is grab the value from the query string and save it off into a local variable.

$url = $_GET['url'];

Then open the url into a file variable using fopen():

$file = fopen ($url, "r");

If it was successful the requested document is now stored in the local variable $file.

Next, to serve the results back to the requestor, in this case the XMLHttpRequest. Because it is an XML request, set the response header accordingly:

header('Content-Type: text/xml');

Loop through the file and echo its contents back. This can be done in a bunch of different ways. I’m doing it this way:

while (!feof ($file))
{
$line = fgets ($file, 1024);
echo $line;
}

Finally, close the file.

fclose($file);

This is a very simplistic example, almost completely devoid of error checking, sanitization and security (see below). But it is a simple example that works.

On security, Jacob Santos points out:

So basically, someone could do http://www.yourdomain.com/fetch.php?url=fetch.php

to get the contents of the script to check and see if it has any venerabilities and it does. Then they can merrily try combinations to get the password file.

http://www.yourdomain.com/fetch.php?url=/../config.php
http://www.yourdomain.com/fetch.php?url=config.php
http://www.yourdomain.com/fetch.php?url=index.php

Yeah, you probably could mention that no one should use the script without either checking that it is in fact a url.

My response:

Jacob, you are absolutely right. This example is not meant to be taken as a prepackaged script and run. It is merely a simple proof of concept of how a PHP-based proxy can work.

In fact, really this is just the barebones pieces of some code I had used to proxy XML documents (notice how I am speaking above in terms of fetching an XML document), when really fopen() will open any document, whether it be a URL, or, as you point out, a file on the local filesystem.

Strictly speaking, using unfiltered $_GET, $_REQUEST or $_POST variables as I do in the example is never a good plan.

So everyone be warned, this example has no error-checking nor does it have any security, which are both concepts beyond the scope of what I’m trying to convey with this post.

And thanks Jacob for bringing this up.