dgo.to/@samuel.mortenson
@mortensonsam
Yell at me later for....
SQL Injection hasn't been seen in core since 2014 (you might remember that one)
(XSS)
Every variable rendered normally is automatically filtered 👍
Attributes are also safe, in general
<b {{ attributes.addClass('"onmouseover="alert(1)"') }}>Hello</b>
> <b class=""onload="alert"">Hello</b>
<b class={{ configurable_class }}>Hello</b>
$variables['configurable_class'] = 'foo onclick=alert(bar)';
> <b class=foo onclick=alert(bar)>Hello</b>
Variables are escaped with the XSS filter, but aren't aware of the context they're used in.
Attribute objects escape the ", &, ', <, and > characters, which is pretty darn safe.
$build = ['#type' => 'html_tag', '#tag' => 'a', 'Hello'];
$build['#attributes']['href'] = $user_input;
$build['#markup'] = $user_input;
$build['#allowed_tags'] = ['script'];
$build['#children'] = $user_input;
$user_input = 'javascript:alert("foo")';
> <a href="javascript:alert(\"foo\")">Hello</a>
$build['#markup'] = t($user_input)
All of these allow <script>...</script>
$build = ['#type' => 'inline_template', '#template' => $user_input];
var value = $('input.title').val();
$('.error').html('<p>Invalid title "' + value + '"</p>');
var value = $('input.title').val();
$('.error').text('Invalid title "' + value + '"');
var value = $('input.title').val();
$('.error').html(Drupal.t('<p>Invalid title "@title"</p>', {'@title': value});
Better
Best
Question complexity
Use Twig wherever possible
If you really need to manually escape user input, use standard methods:
(SQLi)
$uuid = \Drupal::database()
->select('node')
->fields('node', ['uuid'])
->condition('nid', $nid)
->execute()
->fetchField();
SELECT node.uuid AS uuid
FROM
node node
WHERE nid = :db_condition_placeholder_0
$nids = \Drupal::entityQuery('node')
->condition('title', $title)
->execute();
SELECT base_table.vid AS vid, base_table.nid AS nid
FROM
node base_table
INNER JOIN node_field_data node_field_data ON node_field_data.nid = base_table.nid
WHERE node_field_data.title LIKE :db_condition_placeholder_0 ESCAPE '\\'
\Drupal::database()
->query("DELETE FROM people WHERE name = \"$name\"")
->execute();
\Drupal::database()
->query('DELETE FROM people WHERE name = :name', [
':name' => $name,
])
->execute();
$name = 'myname" OR "" = "';
> DELETE FROM people WHERE name = "myname" OR "" = ""
Using placeholders
$result = \Drupal::database()
->delete('people')
->condition('name', '%_' . $name, 'LIKE')
->execute();
$name = '%';
> DELETE FROM people WHERE name LIKE "%_%"
$database = \Drupal::database();
$result = $database
->delete('people')
->condition('name', '%_' . $database->escapeLike($name), 'LIKE')
->execute();
Escaping like
$result = \Drupal::database()
->select('people')
->condition('name', $name, $operator)
->execute();
$name = 'bar';
$operator = 'IS NOT NULL)
UNION ALL SELECT SID,SSID FROM SESSIONS
JOIN USERS WHERE ("foo" <>';
> SELECT FROM people WHERE (name IS NOT NULL)
UNION ALL SELECT SID,SSID FROM SESSIONS
JOIN USERS WHERE ("foo" <> "bar")
Use placeholders everywhere
Try not to write queries by hand
Escape conditions when using LIKE
(RCE)
But you should watch out for the classics
`magick convert $filename output.png`;
shell_exec("magick convert $filename output.png");
unserialize($user_input);
eval("is_null($user_input)");
shell_exec('magick convert ' . escapeshellarg($filename) . ' output.png');
unserialize($user_input, ['allowed_classes' => FALSE]);
{{ content }}
{{ {'#markup': '<b>Render arrays are cool!</b>'} }}
{{ {"#lazy_builder": ["shell_exec", ["touch /tmp/hello"]]} }}
(Mitigated in Drupal 9)
😱
😱
Watch out for dynamic shell commands
Never, ever, ever unserialize user input
Try not to use eval or any equivalent
(IDOR)
Check access when referencing things - for example Entity access, permission checks, and owner checks.
(SSRF)
Ensure that all URLs are validated before executing an HTTP call.
If you have to let users give you URLs to request, ensure they're https://, and external or better yet proxied.
(ALOT) 😉
Use core's File API whenever possible. If you need to do custom operations, strictly validate user paths.
For example, check the protocol (phar://_, check for path traversal (../../), check extension (.php), check filename (.htaccess)
@mortensonsam / Slack @samuel.mortenson / mortenson.coffee