Auditing Drupal Modules for Cross Site Scripting Vulnerabilities
25 March 2010
About XSS
Cross site scripting (XSS) is a pervasive problem in Drupal because the development team takes the approach that data should be sanitized upon display rather than input. The rational for this decision is to maintain data integrity despite translation or manipulation. This is a somewhat non-standard approach in web application circles and leads to no small amount of confusion about "trusted" data sources and the display of data. In general, all user supplied data must be filtered upon display. Drupal provides several useful API calls to facilitate this transformation. These include, but are not limited to, filter_xss(), check_plain(), and the t() function. Drupal output sanitization functions must be used carefully and properly, however, especially the t() function as misuse can introduce unexpected vulnerabilities.Impact
Cross site scripting can have an incredibly damaging effect on a Drupal site. This is mainly due to the fact that a script can utilize AJAX and other functions to create a same domain request forgery. This is distinct from a cross site request forgery (XSRF) in that the user is actually logged into and browsing the domain. Because the Drupal user administration doesn't require re-entry of a password in order to implement changes, any JavaScript on a Drupal site can silently update a user's password if they request the JavaScript. This means that in addition to introducing malware, redirecting users, or other nefarious interactions, arbitrary script on a Drupal site can actually interact with the system allowing an XSS vulnerability to escalate into an account compromise or even a site compromise.Types of XSS
There are generally two classifications of cross site scripting, stored and reflected. A stored cross site scripting vulnerability is one that persists on the site (usually in the database) so that every user requesting a page will be exposed to the attack. A reflected cross site scripting vulnerability is one that requires input from the requestor in order to induce the vulnerability. In a reflected XSS malicious users usually induce victims to click on or otherwise follow links that contain additional URL parameters. This type of attack can also be used against web crawlers, otherwise known as spiders. This is usually done to inject HTML links into page display if the crawler follows a certain link, thus causing the crawler to evaluate your Drupal site as though it contained a link to another site, usually to boost the other site's ranking or search relevance.Examples
When reviewing code for XSS vulnerabilities you want to be sure to check for any occurrence of user supplied data that is being rendered. This not only includes obvious user supplied data such as form data or URL variables, but also less apparent user supplied data such as browser referer values, node titles, and usernames. Any data that users can maniuplate could become a target for script injection. It also bears mentioning that injections do not necessarily have to include the "" so that when alerts appear you can easily spot the offending form field. Of course, once an input is identified you have to move toward tracking down the source. Usually searching the source code for the help text or other display around the form field is useful. Drupal uses the form api to compose many of the forms so searching for specific display text is the key to finding the code that invokes a specific form field. Of course, most XSS actually manifests during display, so you should look outside of the form that actually allows for input of the data to the places where the XSS is rendered. Sometimes this can take a little digging. Additionally, there are some modules that allow for input of data but handle sanitization perfectly well and it is only with the addition of other modules or themes that problems creep up. This is especially true of XSS that you might find in a complex module such as Views, or Bibliography. This process can be quite time consuming, but extremely accurate. The Tag Order module is a great example of a module that requires a complex interaction of inputs to trigger a cross site scripting vulnerability as described in http://drupal.org/node/745386. By creating a new taxonomy with the name "" from the Administer -> Content management -> Taxonomy screen you exploit a stored cross site scripting vulnerability. This condition is triggered when someone goes to the administration screen for the Tag Order module at Administer -> Site configuration -> Tag Order Settings. Looking at this screen makes the vulnerability obvious, but finding it in code is a bit trickier. The simplest way is to search through the module code for the string "The entry order for these vocabularies should be remembered." We find this string in line 37 in the function:function tagorder_admin_settings() { $vocabs = taxonomy_get_vocabularies(); $vocabulary = array(); foreach ($vocabs AS $vocab) { $vocabulary[$vocab->vid] = $vocab->name; } $form['tagorder_vocab'] = array( '#type' => 'checkboxes', '#title' => t('The entry order for these vocabularies should be remembered.'), '#options' => $vocabulary, '#default_value' => variable_get('tagorder_vocab', array('tags')), ); $form['array_filter'] = array('#type' => 'hidden'); return system_settings_form($form); }You can easily see that the $vocabulary variable isn't being sanitized on output. Simply surrounding the $vocab->name in a check_plain() mitigates this vulnerability. Referring to the module CVS documentation at http://drupalcode.org/viewvc/drupal/contributions/modules/tagorder/tagorder.module?r1=1.2&r2=1.2.2.1 you can clearly see that this is exactly what was done to fix this vulnerability.