LET’S WRITE SECURE DRUPAL CODE! TATAR BALAZS JANOS DRUPAL CAMP POLAND WROCLAW, POLAND – 01.06.2019
Who am I? Tatar Balazs Janos @tatarbj Works with Drupal since 2007 CTO @ Petend Drupal Security Correspondent @ EC Active mentor @ Mentoring community group Provisional member @ Drupal Security Team SecOSdreamer @ Secure Open Source day
Tatar Balazs Janos - @tatarbj
Are there site builders? Tatar Balazs Janos - @tatarbj
Tatar Balazs Janos - @tatarbj
Demo Tatar Balazs Janos - @tatarbj
Gist https://gist.github.com/tatarbj/4aa90ba303938aa59969fcf06323f8d1 Tatar Balazs Janos - @tatarbj
Are there developers/maintainers? Tatar Balazs Janos - @tatarbj
Have you attended on a previous Let’s write secure Drupal code! session? Tatar Balazs Janos - @tatarbj
DrupalCamp Antwerp 2017 DrupalCamp Ruhr 2018 DrupalDevDays 2018 Drupal Europe 2018 DrupalCamp Oslo 2018 DrupalCamp London 2019 Drupal Mountain Camp 2019 DrupalCamp Spain 2019 DrupalCamp Belarus 2019 DrupalCamp Kyiv 2019 DrupalCamp Poland 2019 History Tatar Balazs Janos - @tatarbj
Trends in Security Tatar Balazs Janos - @tatarbj
Tatar Balazs Janos - @tatarbj
Tatar Balazs Janos - @tatarbj
Types of vulnerabilities Tatar Balazs Janos - @tatarbj
Tatar Balazs Janos - @tatarbj
Cross Site Scripting Tatar Balazs Janos - @tatarbj
Client side vulnerability Unfiltered output Never trust any user input. We’ve seen the demo before ;) Cross Site Scripting Tatar Balazs Janos - @tatarbj
Tatar Balazs Janos - @tatarbj
Html::escape() – plain text Xss::filter() – html is allowed Xss::filterAdmin() – text by admins Tatar Balazs Janos - @tatarbj
Bingo Tatar Balazs Janos - @tatarbj
Everyone has a bingo card (check your bag!) If you answer well, mark the number! Wrong answer = no number! First who shouts BINGO! wins the price! Rules and etiquette Tatar Balazs Janos - @tatarbj
Round 1 Tatar Balazs Janos - @tatarbj
function custom_field_formatter_view(...) { foreach ($items as $key => $value) { //... $element[$key] = array( '#type' => 'markup', '#markup' => t('<img src="!src" alt="@alt" />', array('!src' => $value['src'], ‚$alt’ => $value['alt'])), ); //... } return $element; } Tatar Balazs Janos - @tatarbj
function custom_field_formatter_view(...) { foreach ($items as $key => $value) { //... $element[$key] = array( '#type' => 'markup', '#markup' => t('<img src="!src" alt="@alt" />', array('!src' => $value['src'], ‚$alt’ => $value['alt'])), ); //... } return $element; } Tatar Balazs Janos - @tatarbj
function custom_field_formatter_view(...) { foreach ($items as $key => $value) { //... $element[$key] = array( '#type' => 'markup', '#markup' => t('<img src="@src" alt="@alt" />', array('@src' => $value['src'], ‚$alt’ => $value['alt'])), ); //... } return $element; } Tatar Balazs Janos - @tatarbj
12 Tatar Balazs Janos - @tatarbj
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; Tatar Balazs Janos - @tatarbj
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; Tatar Balazs Janos - @tatarbj
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; Tatar Balazs Janos - @tatarbj
23 Tatar Balazs Janos - @tatarbj
In Drupal 7 check_url calls check_plain to sanitize bad protocols properly. ? Tatar Balazs Janos - @tatarbj
In Drupal 7 check_url calls check_plain to sanitize bad protocols properly. ? Tatar Balazs Janos - @tatarbj
In Drupal 7 check_url calls drupal_dangerous_protocols to sanitize bad protocols properly. ? Tatar Balazs Janos - @tatarbj
25 Tatar Balazs Janos - @tatarbj
Drupal 8 allows Full HTML to be used by authenticated and administrator users. ? Tatar Balazs Janos - @tatarbj
Drupal 8 allows Full HTML to be used by authenticated and administrator users. ? Tatar Balazs Janos - @tatarbj
17 Tatar Balazs Janos - @tatarbj
Tatar Balazs Janos - @tatarbj
Use behat/automated tests. <script>alert('XSS')</script> <img src="a" onerror="alert('title')"> Check your filters and user roles. Do not give too many options to untrusted users! Protection against Cross Site Scripting Tatar Balazs Janos - @tatarbj
Access Bypass Tatar Balazs Janos - @tatarbj
User can access/do something. Menu items can be defined to be accessed/denied. Many access systems: node, entity, field, views... Access bypass Tatar Balazs Janos - @tatarbj
Round 2 Tatar Balazs Janos - @tatarbj
<?php $query = db_select('node', 'n') ->fields('n', array('title', 'nid')) ->condition('type', 'article'); $result = $query->execute(); ?> Tatar Balazs Janos - @tatarbj
<?php $query = db_select('node', 'n') ->fields('n', array('title', 'nid')) ->condition('type', 'article'); $result = $query->execute(); ?> Tatar Balazs Janos - @tatarbj
<?php $query = db_select('node', 'n') ->fields('n', array('title', 'nid') ->condition('type', 'article') ->addTag('node_access'); $result = $query->execute(); ?> Tatar Balazs Janos - @tatarbj
29 Tatar Balazs Janos - @tatarbj
mymodule.not_found: path: '/not-found' defaults: _controller: 'DrupalmymoduleControllerNotFoundController::build404' _title: 'Page not found' requirements: _access: 'TRUE' Tatar Balazs Janos - @tatarbj
mymodule.not_found: path: '/not-found' defaults: _controller: 'DrupalmymoduleControllerNotFoundController::build404' _title: 'Page not found' requirements: _access: 'TRUE' Tatar Balazs Janos - @tatarbj
16 Tatar Balazs Janos - @tatarbj
Restricted permissions make Drupal sites more secure by calling restrict_permission() method. ? Tatar Balazs Janos - @tatarbj
Restricted permissions make Drupal sites more secure by calling restrict_permission() method. ? Tatar Balazs Janos - @tatarbj
Restricted permissions make Drupal sites more secure by raising attention on the permission page. ? Tatar Balazs Janos - @tatarbj
6 Tatar Balazs Janos - @tatarbj
A standard Drupal 8 allows users to mistype their passwords 10 times. ? Tatar Balazs Janos - @tatarbj
A standard Drupal 8 allows users to mistype their passwords 10 times. ? Tatar Balazs Janos - @tatarbj
A standard Drupal 8 allows users to mistype their passwords 5 times. ? Tatar Balazs Janos - @tatarbj
9 Tatar Balazs Janos - @tatarbj
Tatar Balazs Janos - @tatarbj
Visit node/nid and other urls Visit anything/%node Use behat/automated tests. node_access, entity_access Menu definitions user_access for permissions $query->addTag('node_access') Protection against Access bypass Tatar Balazs Janos - @tatarbj
SQL Injection Tatar Balazs Janos - @tatarbj
Unauthorized access to database resources. Do not trust any user input. SA-CORE-2014-005 – Highly critical D7 SA SQL Injection Tatar Balazs Janos - @tatarbj
Round 3 Tatar Balazs Janos - @tatarbj
<?php $result = Drupal::database() ->delete('people') ->condition('name', '%_' . $_GET['param'], 'LIKE'); ->execute(); ?> Tatar Balazs Janos - @tatarbj
<?php $result = Drupal::database() ->delete('people') ->condition('name', '%_' . $_GET['param'], 'LIKE'); ->execute(); ?> Tatar Balazs Janos - @tatarbj
<?php $database = Drupal::database(); $result = $database ->delete('people') ->condition('name', $database->escapeLike($_GET['param']), 'LIKE'); ->execute(); ?> Tatar Balazs Janos - @tatarbj
31 Tatar Balazs Janos - @tatarbj
A highly critical Drupal 7 core update remediated a Remote code execution vulnerability in 2014. ? Tatar Balazs Janos - @tatarbj
A highly critical Drupal 7 core update remediated a Remote code execution vulnerability in 2014. ? Tatar Balazs Janos - @tatarbj
A highly critical Drupal 7 core update remediated an SQL injection vulnerability in 2014. ? Tatar Balazs Janos - @tatarbj
15 Tatar Balazs Janos - @tatarbj
Tatar Balazs Janos - @tatarbj
Use always drupal Database API! db_query with :placeholder (deprecated in D8, in D9 will be removed) Filter parameters Check the queries in code. username' AND 1=1 POST requests by curl Protection against SQL Injection Tatar Balazs Janos - @tatarbj
Round 4 Ready for some other code? Tatar Balazs Janos - @tatarbj
<?php function _generate_password($length = 8) { $pass = ’’; for ($i = 0; $i < $length; $i++) { // Each iteration, pick a random character from the // allowable string and append it to the password: $pass .= $allowable_characters[mt_rand(0, $len)]; } } ?> Tatar Balazs Janos - @tatarbj
<?php function _generate_password($length = 8) { $pass = ’’; for ($i = 0; $i < $length; $i++) { // Each iteration, pick a random character from the // allowable string and append it to the password: $pass .= $allowable_characters[mt_rand(0, $len)]; } } ?> Tatar Balazs Janos - @tatarbj
<?php function _generate_password($length = 8) { $pass = ’’; for ($i = 0; $i < $length; $i++) { do { // Find a secure random number within the range needed. $index = ord(drupal_random_bytes(1)); } while ($index > $len); $pass .= $allowable_characters[$index]; } } ?> Tatar Balazs Janos - @tatarbj
8 Tatar Balazs Janos - @tatarbj
// custom_module.permissions.yml administer custom module: title: 'Bypass access control' description: 'Allows a user to bypass access control.’ // custom_module.routing.yml custom_module.settings.form: path: '/admin/config/custom/settings' requirements: _permission: 'administer custom module' Tatar Balazs Janos - @tatarbj
// custom_module.permissions.yml administer custom module: title: 'Bypass access control' description: 'Allows a user to bypass access control.’ // custom_module.routing.yml custom_module.settings.form: path: '/admin/config/custom/settings' requirements: _permission: 'administer custom module' Tatar Balazs Janos - @tatarbj
// custom_module.permissions.yml administer custom module: title: 'Bypass access control' description: 'Allows a user to bypass access control.’ restrict access: TRUE // custom_module.routing.yml custom_module.settings.form: path: '/admin/config/custom/settings' requirements: _permission: 'administer custom module' Tatar Balazs Janos - @tatarbj
20 Tatar Balazs Janos - @tatarbj
// contrib_module.routing.yml contrib_module.settings.form: path: '/admin/config/contrib/settings' requirements: _permission: 'administer site configuration' Tatar Balazs Janos - @tatarbj
// contrib_module.routing.yml contrib_module.settings.form: path: '/admin/config/contrib/settings' requirements: _permission: 'administer site configuration' Tatar Balazs Janos - @tatarbj
26 Tatar Balazs Janos - @tatarbj
CSRF stands for Cross Site Remote Fallout vulnerability. ? Tatar Balazs Janos - @tatarbj
CSRF stands for Cross Site Remote Fallout vulnerability. ? Tatar Balazs Janos - @tatarbj
CSRF stands for Cross Site Request Forgery vulnerability. ? Tatar Balazs Janos - @tatarbj
32 Tatar Balazs Janos - @tatarbj
SQL Injection is a server side vulnerability. ? Tatar Balazs Janos - @tatarbj
SQL Injection is a server side vulnerability. ? Tatar Balazs Janos - @tatarbj
13 Tatar Balazs Janos - @tatarbj
Cross Site Scripting vulnerability is in the TOP3 of OWASP list from 2017. ? Tatar Balazs Janos - @tatarbj
Cross Site Scripting vulnerability is in the TOP3 of OWASP list from 2017. ? Tatar Balazs Janos - @tatarbj
Cross Site Scripting vulnerability is not in the TOP3 of OWASP list from 2017, but was in 2013. ? Tatar Balazs Janos - @tatarbj
5 Tatar Balazs Janos - @tatarbj
In case of no winner, extra numbers are coming! ! Tatar Balazs Janos - @tatarbj
18 Tatar Balazs Janos - @tatarbj
27 Tatar Balazs Janos - @tatarbj
30 Tatar Balazs Janos - @tatarbj
1 Tatar Balazs Janos - @tatarbj
11 Tatar Balazs Janos - @tatarbj
33 Tatar Balazs Janos - @tatarbj
Tatar Balazs Janos - @tatarbj
Security Improvements Tatar Balazs Janos - @tatarbj
*https://events.drupal.org/sites/default/files/slides/pwolanin-2017-09-ways-drupal8-d.pdf Many ways Drupal 8 is more secure!* Twig templates for HTML generation Removed PHP format Site configuration exportable, versionable User content entry and filtering improvements User session and session always in ID handling Automated CSRF token protection Trusted host patterns enforced for requests Single statement execution for SQL Clickjacking protection Content security policy compatibility with Core Javascript API Tatar Balazs Janos - @tatarbj
Learn by Advisories Tatar Balazs Janos - @tatarbj
Security advisories are for  Only stable modules  No alpha, beta, dev  d.org hosted projects @Maintainers: If you are contacted, be supportive!  Drupal Security Team Tatar Balazs Janos - @tatarbj
Tatar Balazs Janos - @tatarbj
Hacked! Security review (simplytest.me) Password policy Encrypt Composer Security Checker Permission report Drop Guard Security Awareness programs + PHPCS Drupal BestPractice Sniff Security related projects Tatar Balazs Janos - @tatarbj
Tatar Balazs Janos - @tatarbj
SecOSdays 25-26 OCTOBER, 2019 - SOFIA, BULGARIA Call For Sessions and Sponsors are open! Tatar Balazs Janos - @tatarbj
Questions? Tatar Balazs Janos - @tatarbj
Tatar Balazs Janos @tatarbj Thank you!

Let's write secure Drupal code! - Drupal Camp Poland 2019

Editor's Notes

  • #11 Einstein said: “insanity is when you do the same thing over and over again and expect different results”
  • #13 Owasp: open web application security project
  • #14 Reference for the XSS issue that was basically caused by a security misconfiguration.
  • #24 Hide enabled blocks from selector that are used Context update from this wednesday
  • #25 Hide enabled blocks from selector that are used Context update from this wednesday
  • #26 Hide enabled blocks from selector that are used Context update from this wednesday
  • #64 Not because of having db_query deprecated, but: The $field param is used to derive various table and field names, but in each case the Database API automatically escapes these values. Note that the API does not do this for all arguments!
  • #65 Not because of having db_query deprecated, but: The $field param is used to derive various table and field names, but in each case the Database API automatically escapes these values. Note that the API does not do this for all arguments!
  • #66 Not because of having db_query deprecated, but: The $field param is used to derive various table and field names, but in each case the Database API automatically escapes these values. Note that the API does not do this for all arguments!
  • #76 Mt_rand is not secure enough!
  • #77 Insecure randomness by Mass Password Reset (SA-CONTRIB-2018-043) by Greg Knaddison