In Twig, it's possible to do (and core does this in a few places) code like this, from breadcrumb.html.twig, that directly prints variables inside of an HTML attribute (esp. URLs in <a href> and <img src>):
<a href="{{ item.url }}">{{ item.text }}</a>
As long as the preprocess function (like template_preprocess_breadcrumb() is calling the proper URL sanitization functions (in this case $link->getUrl()->toString()), that {{ item.url }} is fine. However, there may be an expectation that Twig does this URL sanitization for you, since it does do this for HTML text, and therefore people might accidentally introduce XSS vectors in their code.
This is not a new problem. In Drupal 7, this was also your responsibility. And even in Twig, all Twig authors have to deal with this, either by piping to the url_encode or |e('html_attr'), or using the attribute object, or dealing with it in the prepping code.
What's new to Drupal 8 is that preprocess functions no longer need to make variables used within HTML text or fragments safe, as that gets autoescaped, so there is a bit of an inconsistency currently that preprocess functions in HEAD explicitly sanitize URLs, but not anything else, and a corollary inconsistency in which variables Twig templates print via the attributes object and which ones it prints directly into attribute values, such as <a href="{{ item.url }}">.
Possible things we could do:
- Nothing. Trust people new to D8 will read the Twig docs about this issue.
- Document cases like the above (there are lots of them) in the template and explain why they are safe.
- Either override or submit upstream an override for html_attr that does protocol filtering as well so it can be used for either URLs or strings
- Other?
| Comment | File | Size | Author |
|---|---|---|---|
| #7 | unescaped-span.png | 42.82 KB | effulgentsia |
Comments
Comment #2
joelpittetAdding some related issues.
Comment #3
effulgentsia commentedNote that it is also not entirely new to Drupal 8 relative to Drupal 7. In Drupal 7, preprocess functions are expected to make all .tpl.php variables safe. But, what's new to Drupal 8 is that preprocess functions no longer need to make variables used within HTML text or fragments safe, as that gets autoescaped, so there is a bit of an inconsistency currently that preprocess functions in HEAD explicitly sanitize URLs, but not anything else, and a corollary inconsistency in which variables Twig templates print via the
attributesobject and which ones it prints directly into attribute values, such as<a href="{{ item.url }}">.Comment #4
webchickClarified that. Also, this issue title was missing a verb, which assaulted my inner grammar nerd, so fixed that too.
Comment #5
effulgentsia commentedRestored
<code>tags in the IS.Comment #6
catchJust a note this isn't just about URLs, it's about any HTML attribute in a template - given that Twig won't auto-escape variables marked as safe that were filtered as opposed to escaped.
Comment #7
effulgentsia commentedRight. So for example, suppose you were to change bartik's node.html.twig to print the node title into an attribute value, like this:
Because {{ label }} contains a
<span>element (for QuickEdit), you would get this:Even if this example isn't an XSS vulnerability, it still leads to invalid HTML and renders to the browser in a way that isn't desired.
Comment #8
fabianx commentedEven if we tried hard to avoid using '|escape' in templates, I think this is one where it is beneficial for themers to learn it.
So we would teach to use:
with docs that the Attribute object takes care of that automatically.
In case url is a SafeStringAttribute() or a SafeString with strategy = 'html_attr' then twig would not do double-escaping.
Overall that kind of support would be pretty simple to add to drupalEscapeFilter() in TwigExtension.
One way to also do this is to use a link, though I would myself have to lookup how to add the label in (will edit later)
{{ link('text', url) }}Of course writing direct HTML is nicer, but maybe we should allow access to create attribute objects on the fly in twig. Then you could choose:
a)
b)
In one case the Attribute object would take over the escaping, in the other you are yourself responsible.
In any case extending SafeString to either take a strategy parameter or have a class in descending order of safeness for each escaping strategy sounds a good idea to me and makes both of this possible.
Comment #9
fabianx commentedActually a better example should be:
and shows that maybe having magic attributes as a 90% API is better - if I get that wrong on first try ;).
Overall the following escaping strategies are supported in Twig:
(or a PHP callback that takes the template "filename" and must return the escaping strategy to use)
I would say that Urls itself should have the SafeUrlStringInterface implemented or something like that ...
Comment #10
joelpittetThanks @Fabianx. I agree, I do think we need to let Twig's security rule in the twig file.
It could prove tricky to change how those escape strategies work without going upstream pull requests, or a request to make them easier to change. So crossing my fingers a bit on them being acceptable as-is.
#9 this is a good example to work from and add to our documentation:
<a href="{{ url|e('url') }}" rel="bookmark" title="{{ label|e('html_attr') }}" style="{{ some_css|e('css') }}">{{ label }}</a>And agree with creating a
create_attribute({'id': var})would be a great addition because right now we can only create newAttributeobjects through PHP.Comment #11
star-szrBased on the Twig docs the 'url' strategy is not workable for full URLs. html_attr is probably fine for non-URL attributes.
Unless this part changes I think we should instead add our own strategies.
create_attribute() is interesting, probably not a bad idea.
Comment #12
David_Rothstein commentedComment #24
smustgrave commentedSince it's been 7 years without movement
1. Wonder if this is still a valid task
2. If so what needs to be done for D10
Worth noting #2544110: XSS attribute filtering is inconsistent and strips valid attributes is being worked on and not sure how it plays here.
Comment #26
smustgrave commented