6.2. Remote File Risks
PHP has a configuration directive called allow_url_fopen that is enabled by default. It allows you to reference many types of resources as though they were local files. For example, you can retrieve the content (HTML) of a particular page by reading from a URL:
<?php $contents = file_get_contents('http://example.org/'); ?>
As discussed in Chapter 5, this can create severe vulnerabilities when tainted data is used to reference a file in include or require statements. In fact, I consider this particular type of vulnerability to be one of the most dangerous vulnerabilities possible in a PHP application because it allows an attacker to execute arbitrary code.
Although slightly less severe in magnitude, similar vulnerabilities exist when tainted data is used to reference a file in standard filesystem functions. For example, consider reading a file as follows:
<?php $contents = file_get_contents($_GET['filename']); ?>
This particular example lets a user manipulate the behavior of file_get_contents( ) so that it retrieves the contents of a remote resource. Consider a request similar to the following:
This results in a situation in which $contents is tainted, a fact obscured by the indirect way in which it is obtained. This is another reason why Defense in Depth is such a strong principleby treating the filesystem as a remote source of data, the value of $contents is considered to be input anyway, so your filtering logic can potentially save the day.
Because $content is tainted, it can lead to many other types of security vulnerabilities, including cross-site scripting and SQL injection . For example, the following illustrates a cross-site scripting vulnerability:
<?php $contents = file_get_contents($_GET['filename']); echo $contents; ?>
The solution is to never use tainted data to refer to a filename. Always filter input and be sure to use only filtered data when referencing a filename:
<?php $clean = array(); /* Filter Input ($_GET['filename']) */ $contents = file_get_contents($clean['filename']); ?>
Although this does not guarantee anything about the data within $contents, it does give you reasonable assurance that you are reading a file that you intend to be reading, rather than one chosen by an attacker. To strengthen this approach, you should also treat $contents as input and filter it prior to use:
<?php $clean = array(); $html = array(); /* Filter Input ($_GET['filename']) */ $contents = file_get_contents($clean['filename']); /* Filter Input ($contents) */ $html['contents'] = htmlentities($clean['contents'], ENT_QUOTES, 'UTF-8'); echo $html['contents']; ?>
This provides a very strong defense against numerous types of attacks, and it is the recommended approach.