Let's write secure Drupal code!
Tatar Balazs Janos
18/03/2018 – DrupalCamp Ruhr
Who am I?
Tatar Balazs Janos
Hungarian, lives in Brussels
Works with Drupal since 2007
Provisional Member of Drupal Security Team
Technical PM and former QA Lead @ EC-DIGIT
Site builders?
Let's start with a demo
Developers?
Maintainers?
Issue types - Web
Issue types - Drupal
Trends in Security Advisories
Trends in Security Advisories
Cross site scripting
Handle text in a secure fashion
Drupal 7 way
Examples
Test! 
Raise your green card if snippet is secure!
Raise your red card if code has issues!
<?php print '<tr><td>' . check_plain($title) . '</td></tr>'; ?>
<?php print '<tr><td>' . check_plain($title) . '</td></tr>'; ?>
<?php print l(check_plain($title), 'node/' . $nid); ?>
<?php print l(check_plain($title), 'node/' . $nid); ?>
<?php print l($title), 'node/' . $nid); ?>
<?php print '<a href="/' . check_plain($url) . '">'; ?>
<?php print '<a href="/' . check_plain($url) . '">'; ?>
<?php print '<a href="/' . check_url($url) . '">'; ?>
foreach ($items as $delta => $item) {
$id = $item->getValue()['target_id'];
$content = Drupal::entityTypeManager()
->getStorage($entity_type_id)
->load($id);
$body = $content->get('body_field')->getValue()[0]['value'];
}
$elements[$delta] = array(
'#theme' => 'something_custom',
'#body' => $body,
);
return $elements;
foreach ($items as $delta => $item) {
$id = $item->getValue()['target_id'];
$content = Drupal::entityTypeManager()
->getStorage($entity_type_id)
->load($id);
$body = $content->get('body_field')->getValue()[0]['value'];
}
$elements[$delta] = array(
'#theme' => 'something_custom',
'#body' => $body,
);
return $elements;
foreach ($items as $delta => $item) {
$id = $item->getValue()['target_id'];
$content = Drupal::entityTypeManager()
->getStorage($entity_type_id)
->load($id);
$body = [
'#type' => 'processed_text',
'#text' => $content->get('body_field')->getValue()[0]['value'],
'#format' => $content->get('body_field')->getValue()[0]['format'], ];
}
$elements[$delta] = array(
'#theme' => 'something_custom',
'#body' => $body,
);
return $elements;
Twig in drupal8
No SQL queries
No access to drupal APIs
Automatic escaping
Sanitization in drupal8
Html::escape() – plain text
Xss::filter() – html is allowed
Xss::filterAdmin() – text by admins
@variable – string or MarkupInterface object
%variable – wrapped in <em>
:variable – url for href
Testing XSS
<script>alert('xss')</script>
<img src="a" onerror="alert('title')" >
Catches 90%
Access Bypass
User can access/do something
hook_menu()
Node access system
Entity access
Field access
Views access control
Test again 
<?php
function mymodule_menu() {
$items['admin/mymodule/settings'] = array(
'title' => 'Settings of my module',
'page callback' => 'drupal_get_form',
'page arguments' => array('mymodule_setting_form'),
'access arguments' => array('administer mymodule'),
'type' => MENU_LOCAL_ACTION,
);
return $items;
}
?>
<?php
function mymodule_menu() {
$items['admin/mymodule/settings'] = array(
'title' => 'Settings of my module',
'page callback' => 'drupal_get_form',
'page arguments' => array('mymodule_setting_form'),
'access arguments' => array('administer mymodule'),
'type' => MENU_LOCAL_ACTION,
);
return $items;
}
?>
<?php
$query = db_select('node', 'n')
->fields('n', array('title', 'nid')
->condition('type', 'article');
$result = $query->execute();
?>
<?php
$query = db_select('node', 'n')
->fields('n', array('title', 'nid')
->condition('type', 'article');
$result = $query->execute();
?>
<?php
$query = db_select('node', 'n')
->fields('n', array('title', 'nid')
->condition('type', 'article')
->addTag('node_access');
$result = $query->execute();
?>
mymodule.not_found:
path: '/not-found'
defaults:
_controller: DrupalmymoduleControllerNotFoundController::build404
_title: 'Page not found'
requirements:
_access: 'TRUE'
mymodule.not_found:
path: '/not-found'
defaults:
_controller: DrupalmymoduleControllerNotFoundController::build404'
_title: 'Page not found'
requirements:
_access: 'TRUE'
Testing access bypass
Visit node/nid and other urls
Visit anything/%node
Use behat/automated tests
Fixing access bypass
node_access
entity_access
Menu definitions
user_access for permissions
$query->addTag('node_access')
Write automated tests
SQL Injection
Executing SQL of the attacker
Change/gain information
Just a small test 
<?php
$results = db_query("SELECT uid, name, mail FROM {users}
WHERE name LIKE '%%$user_search%%'");
?>
<?php
$results = db_query("SELECT uid, name, mail FROM {users}
WHERE name LIKE '%%$user_search%%'");
?>
<?php
$results = db_query("SELECT uid, name, míail FROM
{users} WHERE name LIKE :user_search",
array(':term' => '%' . db_like($user_search)));
?>
Testing SQL Injection
Check the queries in code
coder.module
username' AND 1=1
POST requests by curl
Fixing SQL Injection
Use always drupal Database API
db_query with :placeholder
(deprecated in D8, in D9 will be removed)
Filter parameters
db_like()
Cross Site Request Forgery
Path-based vulnerability
Missing valid token
<img>
Fixing CSRF
Use Form API
Send and validate token properly
In D8 use the built-in csrf_token
Security improvements in Drupal 8
Twig
No PHP filter in core
Local image filter
WYSIWYG in core (advanced filtering)
Built-in CSRF token mechanism
Learn by advisories
Report issues on security.drupal.org
Security advisories are for
• Only stable modules
• No alpha, beta, dev
• d.org hosted modules
@Maintainers: If you are contacted, be supportive! 
An example
Project applications on drupal.org
New contributors
Release management – stable
Power of the community
Automated (pareview.sh) and manual code review
Security related contrib modules
Hacked!
Security review (simplytest.me)
Paranoia
Password policy
Encrypt
Drop Guard
Guardian
Composer Security Checker
Permission report
Text format reported
And so on...
+ PHPCS Drupal BestPractice Sniff
Thank you!
Tatar Balazs Janos
@tatarbj
https://www.drupal.org/u/tatarbj
https://twitter.com/tatarbj
Drupal slack: @tatarbj

Let's write secure Drupal code!

Editor's Notes

  • #5 Wrong settings of text filters
  • #8 Owasp = Open Web Application Security Project
  • #30 Placeholders: At - @ Percent - % Colon - :