Skip to content

Instantly share code, notes, and snippets.

@arpitr
Created April 27, 2026 04:52
Show Gist options
  • Select an option

  • Save arpitr/499ea4c69290f6886acb63730bee4dd4 to your computer and use it in GitHub Desktop.

Select an option

Save arpitr/499ea4c69290f6886acb63730bee4dd4 to your computer and use it in GitHub Desktop.

Drupal Security: Injection Attack Prevention

This document demonstrates how Drupal handles common injection attacks using its built-in security utilities.


Utilities Used

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

1. HTML Injection

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);

Output

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() &lt;p&gt;Hello&lt;/p&gt;&lt;script&gt;alert(1)&lt;/script&gt;&lt;strong&gt;World&lt;/strong&gt;

How It Works

  • Xss::filter() — Removes <script> (not in the allowlist) but keeps safe tags like <p> and <strong>. The script body alert(1) is left as plain text.
  • Html::escape() — Encodes every < and > as HTML entities, rendering all tags inert.

2. JavaScript (Event Handler) Injection

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);

Output

Stage Value
Raw Input <img src='x' onerror='alert(document.cookie)'>
Xss::filter() (empty string)
Html::escape() &lt;img src=&#039;x&#039; onerror=&#039;alert(document.cookie)&#039;&gt;

How It Works

  • Xss::filter() — Removes the entire <img> tag. Even though <img> can be safe, the onerror event 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.

3. CSS Injection

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);

Output

Stage Value
Raw Input <style>body { display: none; }</style>
Xss::filter() body { display: none; }
Html::escape() &lt;style&gt;body { display: none; }&lt;/style&gt;

How It Works

  • 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 &lt;style&gt;, fully neutralising the injection.

4. SQL 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();

How It Works

Drupal's Database API uses prepared statements with parameterised queries. The malicious input is never interpolated directly into the SQL string.

Generated Query (via dpq())

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.


Summary

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 ⚠️ Strips tags, leaves text ✅ Fully encodes output —
SQL Injection — — ✅ Parameterised queries

Best Practices

  • 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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment