Do a quick security review when porting your module
Heine Thu, 2007/02/22 - 08:48
Adapted from a mail I sent to the Drupal development list.
Porting a module is an excellent opportunity to keep an eye out for security problems (evidence: DRUPAL-SA-2006-031). Here's a quick security reminder regarding input (user-supplied data). Code samples are only included to make a point, do not hold them against me.
Escape or filter text before display
If you do not escape or filter text before display, you enable a user to insert arbitrary HTML and scripts into pages. This type of attack is know as cross site scripting aka XSS.
For more information, see: Handle text in a secure fashion.
Escape / filter:
check_plain
ortheme('placeholder')
for plain text.check_markup
orfilter_xss
for markup containing text.
Drupal 5 brought a (brilliant IMO) change to t()
:
Depending on the placeholder's sigil, it is passed through theme('placeholder') (%) or check_plain (@) automatically.
Sometimes the difficult part is to know whether a function requires escaped text or does the escaping itself:
Automatically escape:
l()
- escapes text and attributes unless you pass TRUE for the $html parameter. In that case only attributes are check_plained.- menu items & breadcrumbs
- block descriptions (*not* titles)
theme('username')
* Common pitfalls:
watchdog
- you have to provide a safe $messagedrupal_set_title
- you have to provide a safe $titleExamples:
drupal_set_title(check_plain($node->title));
'#type' => 'textfield';
'#default_value' => check_plain($u_supplied), // bad: escaped twice
'#description' => t("Old data: !data", array('!data' => $u_supplied)), // XSS
);
$form['good'] = array(
'#type' => 'textfield';
'#default_value' => $u_supplied,
'#description' => t("Old data: @data", array('@data' => $u_supplied)),
);
Database (ab)use
See also Database access and When to use db_rewrite_sql in the Writing secure code pages.
Summary: Whatever you do, *never* use user supplied data directly in a query. Use db_query
as it was meant to be used: data goes in arguments. And make sure to enclose %s in single quotes: '%s'.
BAD:
db_query("SELECT t.s FROM {table} t WHERE t.field = %s", $from_user); // Will result in an error.
Good:
BAD ($from_user is an array of numbers):
Good:
db_query("SELECT t.s FROM {table} t WHERE t.field IN ($placeholders)", $from_user);
Anything else: bad. Why is this bad? Suppose you check the data that you stuff in to a query (e.g. 'all numeric'). Or, you know a user name is validated to contain only certain 'safe' characters.
The problem is that you open the possibility that a code path develops where unfiltered content ends up at your query. In that case you're toast.
This code path can arise because you 1) accidentally remove validation in future development, 2) a user adds a module that is more lenient in the data it accepts or 3) you add a bunch of code that skips validation (recent xTracker problem).
When you query the database for content to display to users, make sure to use db_rewrite_sql
. For more information see When to use db_rewrite_sql.
Writing secure code
If you want to read more about security, I suggest that you start at Input the root of all evil in the Writing secure code pages.
I hope to expand and improve those pages in the future, but some of them are already useful.
You found a vulnerability?
What to do when you find a vulnerability in your module? Simple; write us at security@drupal.org.