One of the most interesting sessions I attended at the DrupalCon London was "Doing Drupal security right", given by Gábor Hojtsy, a Drupal 6 maintainer involved in the security team.

It was a very useful presentation, with lots of tips and advises that you don't always realise when you write custom code or setup a website environment.

The presentation was based on the "Top 10 Most Critical Web Application Security Risks" guide, written by OWASP, adapting each one of the cases to a Drupal site.

The 10 most critical Drupal security risks

1. SQL Injection

You shouldn't use data that you don't trust (as a feed, a user input, another database, etc) directly in a database query without escaping it:

index.php?id=12

mysql_query("UPDATE mytable SET value = '". $value ."' WHERE id = ". $_GET['id']);

Instead you should use Drupal functions passing the user input as parameters:

db_query("UPDATE {mytable} SET value = :value WHERE id = :id", array(':value' => $value, ':id' => $id);

If you need to include dynamic table or column names in your query, you can use db_escape_table().

2. Cross Site Scripting (XSS)

XSS is injecting data to the HTML output. This is a very important one: there is a 64% likelihood a website has a XSS issue.

You should always escape the variables you send to the output if they come from a non trusted source, like the parameters of a URL. For example, this code is unsecure:

index.php?id=12

print $_GET['id'];

It's also very common see codes like this, specially in custom themes:

$output .= $node->title;

The problem in the code above is that Drupal doesn't usually filter the user input when a node is saved. So the user could save malicious code in the title and then it will be printed without any kind of filtering.



One more obvious mistake is giving full HTML permissions to not trusted users, or allow to use unsafe tags as <script>.

If we grant Full HTML permissions to users, they could add this JS code to a page, which would change the admin password if he views the content:

jQuery.get('/user/1/edit', function (data, status) {

if (status == 'success') {

var p = 'id="edit-user-edit-form-token" value="([a-z0-9]*)"';

var matches = data.match(p);

var token = matches[1];

var payload = {

"form_id": 'user_edit',

"form_token": token,

"pass[pass1]": 'hacked',

"pass[pass2]": 'hacked'

};

jQuery.post('/user/1/edit', payload);

}

});

This technique, with code changes, works up to Drupal 6. (Example from Heine Deelstra, Drupal Security team lead http://heine.familiedeelstra.com/change-password-xss)

So, what can you do? Drupal provides a set of functions you should use:

Use placeholders in functions like t() or format_plural(): %name, @url, !insecure:

t('%name has a blog at <a href=" @url " _fcksavedurl=" @url " _fcksavedurl=" @url " _fcksavedurl=" @url "> @url </a>', array('@url' => valid_url($user->profile_blog), '%name' => $user->name));

Use Drupal.t() , Drupal.formatPlural() in JavaScript.

3. Authentications and sessions

You need to deal with these issues:

Weak password storage and account management

Session hijacking / fixation

Lack of session timeout / logout

Drupal has good solutions for that, so don't need to worry too much about these:

Passwords are stored hashed

Session IDs changed when permissions change

Drupal works with Apache's SSL transport

Modules to set certain URLs to use SSL



4. Insecure direct object references

We sometimes don't check if the user has permission to access objects:

index.php?id=12

db_query("SELECT * FROM {node} WHERE nid = :id", array(':id' => $_GET['id'] ));

When using Views, sometimes we don't make sure the user has access to nodes. One common issue is forget to add "published = Yes" to the view filters.

Drupal approach:

Menu system handles permission checking

user_access('administer nodes', $account);

node_access('edit', $node, $account);

$select->addtag('node_access');

Form API checks for data validity

5. Cross Site Request Forgery (CSRF)

If there is an image like this the user who loads it will be logged out.

<img src="https://example.com/user/logout" />

In the same way some content could be deleted:

<img src="https://example.com/index.php?delete=12" />

That's why Drupal always asks "Are you sure you want to delete this?"

Again, we can avoid this issue doing things in "The Drupal Way":

Form API works with POST submissions by default (makes it harder)

Form API includes form tokens, requires form retrieval before submission, checks valid values

drupal_valid_token() provided to generate/validate tokens for GET requests.

6. Security misconfiguration

Secure server

First you should check security holes behind Drupal, like the server configuration

Avoid using FTP at all cost and check your client tool. There are software to decrypt passwords stored in FTP clients.

In shared servers, know who do you share the server with. You might be sharing hosting with a site of a politic party, which could be the objective of an attack.

Which applications are running on the server? Applications like phpBB2 have lots of security holes.

Keep your OS, PHP, SQL server, etc. up to date.

Secure Drupal

Is your admin password "admin"?

"admin"? Look at all "administer *" permissions

"administer filters" can take over a site

can take over a site Use Update module , watch the security news (security updates are made on Wednesdays)

, watch the security news (security updates are made on Wednesdays) Avoid any kind of PHP input , write your own modules instead. Look into using Paranoia module

, write your own modules instead. Look into using Paranoia module Watch your input formats, you can be googled ! . If we enable Full HTML for anonymous users, somebody can find our site searching the filter description ("Full HTML, Web page addresses and e-mail addresses turn...") and could hack our site.

. If we enable Full HTML for anonymous users, somebody can find our site searching the filter description ("Full HTML, Web page addresses and e-mail addresses turn...") and could hack our site. Check out the security_review module .

7. Insecure cryptographic storage

Drupal approach:

Drupal stores user passwords hashed with a one-way hash

Different randomly generated private key is provided on each site, which can be used to do reversible encryption

Modules exist to help encrypt more data

Up to you to ensure backups are properly protected

8. Failure to restrict URL access

Drupal approach:

Menu system uses access callback and access arguments

Continually review permissions

9. Insufficient transport protection

Tools like Firesheep, get data flowing in the same network you are connected. So somebody connected to the same network as you could see Facebook connections, pictures, etc. You could even login as this user grabbing the login session id.



Drupal approach:

Run Drupal on top of full SSL , which is expensive.

, which is expensive. Use securepages and securepages_prevent_hijack to wall your important pages.

and to wall your important pages. http://drupalscout.com/knowledge-base/drupal-and-ssl-multiple-recipespossible-solutions-https

Use a valid certificate.

10. Unvalidated redirects

Having custom redirect systems can be also unsafe:

http://example.com/index.php?target=evil.com

Drupal approach:

Drupal has various internal redirections, which use local paths and generate URLs based on them

Look for use of drupal_goto() and Form API #redirect instances in your modules to validate their compliance

Resources:

Session video: http://london2011.drupal.org/conference/sessions/doing-drupal-security-right

Session slides: http://london2011.drupal.org/sites/default/files/DrupalSecurity2011London_0.pdf

http://drupal.org/writing-secure-code

Book: Cracking Drupal, A drop in the Bucket (specially chapter 8)

The ten most critical web application security risks: https://www.owasp.org/images/0/0f/OWASP_T10_-_2010_rc1.pdf



Points of contact with the Drupal security team: