This document demonstrates how Drupal handles common injection attacks using its built-in security utilities.
use Drupal\Component\Utility\Xss;
use Drupal\Component\Utility\Html;| Utility | Method | Purpose |
|---|---|---|
Xss |
Xss::filter($input) |
Strips disallowed HTML tags, allows safe ones |
Html |
Html::escape($input) |
Converts all HTML special characters to entities |
Attackers inject malicious HTML tags (e.g., <script>) to execute code in the browser.
$input = '<p>Hello</p><script>alert(1)</script><strong>World</strong>';
print_r($input);
print_r("\n");
$output_xss = Xss::filter($input);
$output_html = Html::escape($input);
print_r($output_xss);
print_r("\n");
print_r($output_html);| Stage | Value |
|---|---|
| Raw Input | <p>Hello</p><script>alert(1)</script><strong>World</strong> |
Xss::filter() |
<p>Hello</p>alert(1)<strong>World</strong> |
Html::escape() |
<p>Hello</p><script>alert(1)</script><strong>World</strong> |
Xss::filter()— Removes<script>(not in the allowlist) but keeps safe tags like<p>and<strong>. The script bodyalert(1)is left as plain text.Html::escape()— Encodes every<and>as HTML entities, rendering all tags inert.
Attackers inject event attributes like onerror to execute JavaScript without a <script> tag.
$input = "<img src='x' onerror='alert(document.cookie)'>";
print_r($input);
print_r("\n");
$output_xss = Xss::filter($input);
$output_html = Html::escape($input);
print_r($output_xss);
print_r("\n");
print_r($output_html);| Stage | Value |
|---|---|
| Raw Input | <img src='x' onerror='alert(document.cookie)'> |
Xss::filter() |
(empty string) |
Html::escape() |
<img src='x' onerror='alert(document.cookie)'> |
Xss::filter()— Removes the entire<img>tag. Even though<img>can be safe, theonerrorevent handler makes it dangerous and it is stripped entirely.Html::escape()— Encodes the tag into a harmless string; the browser displays it as text, never executing it.
Attackers inject <style> blocks to manipulate page layout, hide content, or conduct UI redressing attacks.
$input = '<style>body { display: none; }</style>';
print_r($input);
print_r("\n");
$output_xss = Xss::filter($input);
$output_html = Html::escape($input);
print_r($output_xss);
print_r("\n");
print_r($output_html);| Stage | Value |
|---|---|
| Raw Input | <style>body { display: none; }</style> |
Xss::filter() |
body { display: none; } |
Html::escape() |
<style>body { display: none; }</style> |
Xss::filter()— Strips the<style>tags (not in allowlist), but leaves the raw CSS text behind. While not executable as CSS without the tags, this output should still be treated cautiously.Html::escape()— Encodes the entire string;<style>becomes<style>, fully neutralising the injection.
Attackers embed SQL syntax into input to manipulate database queries — for example, to drop tables or exfiltrate data.
$input = "Robert'); DROP TABLE users;";
$query = \Drupal::database()->select('users_field_data', 'u')
->fields('u')
->condition('name', $input);
dpq($query); // Debug: prints the parameterised query
$result = $query->execute();Drupal's Database API uses prepared statements with parameterised queries. The malicious input is never interpolated directly into the SQL string.
SELECT u.*
FROM {users_field_data} u
WHERE name = :db_condition_placeholder_0
-- Bind: :db_condition_placeholder_0 = "Robert'); DROP TABLE users;"The entire value Robert'); DROP TABLE users; is passed as a bound parameter, so the database engine treats it as a literal string — not executable SQL.
Note:
dpq()is a Drupal development helper that prints the query. It should not be used in production code.
| Attack Type | Xss::filter() |
Html::escape() |
Database API |
|---|---|---|---|
| HTML Injection | ✅ Removes dangerous tags | ✅ Encodes all tags | — |
| JS Event Injection | ✅ Strips unsafe tags/attrs | ✅ Encodes all characters | — |
| CSS Injection | ✅ Fully encodes output | — | |
| SQL Injection | — | — | ✅ Parameterised queries |
- Use
Html::escape()when rendering plain text in HTML contexts — it is the safest option for output encoding. - Use
Xss::filter()only when you intentionally allow a subset of HTML tags (e.g., user-generated rich text). - Always use Drupal's Database API (
select(),insert(), etc.) — never concatenate raw user input into SQL strings. - Remove
dpq()calls before deploying to production.