You are here

Generating safe markup in Drupal 8

Submitted by jbrown on Fri, 09/19/2014 - 19:29

The most insecure part of a Drupal website is typically the theme. Drupal 8 is using Twig as its template layer. This is a massive leap forward. It's no longer possible to put SQL queries in a template file!

Furthermore, Drupal 8 is now taking advantage of a security feature of Twig: autoescape. Every variable in a Twig template will be escaped if it is not marked as safe. This makes it much harder to introduce XSS vulnerabilities.

Unfortunately any HTML that your module produces will end up being double-escaped and the HTML itself will be visible instead of the browser's rendering of it. The quick and dirty way to fix this problem is to wrap your string with \Drupal\Component\Utility\SafeMarkup::set:

<?php
use Drupal\Component\Utility\SafeMarkup;
use
Drupal\Component\Utility\String;
$output = SafeMarkup::set('<div class="my-module">' . String::checkPlain($my_variable) . '<div>');
?>

But this defeats the whole point of using autoescape. The correct approach is that all HTML created by a module should be declared in a Twig template. This means that all the variables are guaranteed to be escaped. It is also very easy to implement.

First you need to declare the template in your hook_theme():

<?php
function my_module_theme(array $existing, $type, $theme, $path) {
  return array(
   
'my_module_my_template' => array(
     
'template' => 'my-template',
     
'variables' => array(
       
'variable1' => NULL,
       
'variable2' => NULL,
      ),
    ),
  );
}
?>

You then need to create a Twig template file, for example my_module/templates/my-template.html.twig:

{#
/**
* @file
* Default theme implementation for my template.
*
* Available variables
* - variable1: The first variable.
* - variable2: The second variable.
*/
#}
<div class="my-template">
  This is the first variable: <b>{{ variable1 }}</b>.
  This is the second variable: <i>{{ variable2 }}</i>.
</div>

When you need to generate your html you should use the drupal_render() function:

<?php
$render
= array(
 
'#theme' => 'my_module_my_template',
 
'#variable1' => t("First"),
 
'#variable2' => t("Second"),
);

$output = drupal_render($render);
?>

Strings returned by drupal_render() are automatically marked as safe and will not be escaped again.

Comments

I would like to stress though that just because a module has the ability to create theme hooks, it probably shouldn't. Instead one should look for existing templates to utilize and just use the "Render API" appropriately to create the necessary collage of theme hooks/templates.

Add new comment