Problem/Motivation
Xss::filter() automatically does HTML escaping and protocol filtering on attributes. Protocols are filtered on everything except title, alt and data-
Attribute however, while it claims to make attributes sanitized and safe (issue to fix the claim at #2567741: Attribute/drupal_attributes() docs do not mention protocol filtering on URLs), does no such protocol filtering.
Steps to reproduce
Proposed resolution
Apply protocol stripping to everything except data attributes and a list of attributes necessary to not break existing tests.
The list could be expanded later in #2544110: XSS attribute filtering is inconsistent and strips valid attributes.
Remaining tasks
User interface changes
Introduced terminology
API changes
Data model changes
Release notes snippet
| Comment | File | Size | Author |
|---|---|---|---|
| #52 | interdiff.txt | 3.84 KB | catch |
| #52 | 2567743-52.patch | 11.54 KB | catch |
| #48 | add_protocol_filtering-2567743-48.patch | 5.46 KB | joelpittet |
| #48 | interdiff.txt | 967 bytes | joelpittet |
| #47 | interdiff.txt | 970 bytes | joelpittet |
Issue fork drupal-2567743
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
Comments
Comment #2
joelpittetThis sounds like a great idea. We already do
Html::escape()on all values and attribute names. Though this may not be the best stategy. Twig actually has a strategy for attributes:http://twig.sensiolabs.org/doc/filters/escape.html
html_attr. I would suggest we use that to it's advantage as well as tag it on to AttributeValueBase. The name part can be leftHtml::escape()i'd assume.If we want to strip dangerous protocols, it may be good to use Twig's
html_attrstrategy in place ofHtml::escape()insideUrlHelper::filterBadProtocol()at the very least I'd suspect, as well.Xss::filter()needs the context of the tag I believe to do it's job effectively. Though we have context in Attribute to know the name of the attribute and effectively be smart about which attributes need something like.UrlHelper::stripDangerousProtocols()eg:href,src,action,etc.And which ones don't eg.titleHope that helps.
Comment #3
joelpittetOh and we should probably let twig apply the 'html_attr' strategy and not do it in Attribute as a step here. So the only domain thing this will deal with the dangerous protocol domain.
Just have to make our escapeFilter a bit smarter about SafeStringIntrface escaping strategy, which was inevitable.
Comment #4
pwolanin commented@joelpittet - doing this all in twig would be a pretty dramatic code change.
Also, I don't think that actually meets the goal of this issue - it seems html_attr is just a much broader conversion of characters to entities:
http://stackoverflow.com/questions/12038048/difference-between-escapehtm...
In contrast, the suggestion here is to do protocol filtering. We can't escape those or they would break href, and src for example.
Comment #5
pwolanin commentedThe tricky part of this issue is that I'm not sure we have the attribute name at the place we need it.
Comment #6
pwolanin commentedI think it just needs to do this?
Probably need to write some new tests for it - let's see if existing ones fail.
Comment #7
effulgentsia commentedPer #2565895-21: Add a new :placeholder to SafeMarkup::format() for URLs that handles bad protocols, I really don't think we should use filterBadProtocol() on something whose API is defined as needing to be in plain-text to begin with (which this is, since we currently do an unconditional Html::escape() on it). Instead, I think we should do
Html::escape(UrlHelper::stripDangerousProtocols($this->value));(without the Html::decodeEntities() that filterBadProtocol() does).Comment #8
effulgentsia commentedNote there's a test in #2567795: Introduce a : placeholder which works like ! for now that includes a URL fragment on which Html::decodeEntities() would be incorrect to do, so perhaps we can add a similar test for that here as well.
Comment #11
pwolanin commented@effulgentsia - per catch, I was trying to mimic what Xss::filter does, which is to call UrlHelper::filterBadProtocol()
Comment #12
effulgentsia commentedThe @param of Xss::filter() explicitly says that the parameter it receives is: "The string with raw HTML in it." So yes, that then needs to decode HTML entities, because what it was passed is expected to be HTML encoded. The input to Attribute, however, is explicitly plain-text, not HTML. Html::decodeEntities() is a function you call on HTML, not on plain text.
Comment #13
dawehnerThis is, on purpose, a test only patch.
Comment #14
catchRunning #13 past the bot.
Comment #16
catchAnd here's what I think we want. I had to whitelist 'callback' due to this amusing fail:
Possibly we should use data-callback so that the AttributeString class doesn't know about placeholdering instead.
I get one fail from Daniel's phpunit test coverage, didn't look into that yet.
Comment #18
catchOK the one fail was a bad comparison in the test, see interdiff.
This should be green, but still a @todo for data-callback.
Comment #23
dawehnermicro opt, I would swap the two conditions around.
The expected output I don't get. Do we really want two "))" closed?
Comment #24
catch#1 So I'd swap them around and additionally try to make it an isset() check (maybe a class property with the whitelist as an assoc array), but wanted to keep the code the same as Xss::attributes() for now. I guess we could update Xss::attributes at the same time though so they still match.
#2 We only strip the protocols, we don't then correct the parentheses after the protocols were stripped.
Comment #25
catchDid that placeholder change.
Added 'value' to the whitelist which should fix the AJAX fails.
No interdiff because fail.
Comment #27
catchAdding name via forum drag and drop tests.
Comment #28
dawehnerCan we expand the test coverage to include an example for alt/value/name, just to be 100% sure?
Comment #29
catchYes I think we need that, although need a full whitelist first, see how #27 does with the bot.
We should probably also apply the same whitelisting to Xss::attributes() too and update the tests there.
Comment #32
catchLess fails.
Comment #34
dawehnerGiven how many we already have I'm curious whether this needs to be configurable, much like allowed protocols ...
Comment #35
catchOne more. And yes I think we need to think about extensibility here.
Leaving in the responsive image fail because nfi.
Comment #36
pwolanin commentedI would rather see us using an isset() instead of much slower in_array()
Comment #37
xjmWhat is the correct way for someone to legitimately create attributes for something that are considered dangerous? I would have assumed that using Attribute() was the API for this.
Comment #39
wim leersComment #40
catch@xjm I think that's what we're running into with:
Don't have a good idea for that yet.
@pwolanin as above yes I think we should do this, but also I think we need to factor this whitelist logic out, make it extensible, and apply it to Xss::attributes() (or decide why it behaves differently here if not).
Comment #41
wim leersThis could (and IMO should) just not use
Attribute, and do aSafeString::create(). This is internal in the Renderer anyway.We should do that, because this is what the preceding comment says:
Prefixing it with
data-makes it less developer-friendly.Comment #42
dawehner@Wim Leers
Do you mind doing that conversion in its own issue?
Comment #45
joelpittetI'd like to add a few test assertions to this if it's ok with you all?
Ça joue?
Comment #46
catchFor the responsive image data: issue and #2531824: Attribute class to check safe strings before escaping (has tests) I think we should consider adding AttributeSafeStringInterface.
This will allow us to mark a string not only as safe, but safe for use in an attribute (a string that's safe for an attribute should be safe in an HTML fragment, but not vice versa).
Then when you have either an already escaped attribute or a valid use case for something like data: you can use it.
But it avoids the case where something is run through Filter:xss(), then assigned to SafeString, then put into an attribute - which if we allowed for that to avoid double-escaping would be its own XSS vector.
More test coverage here would be great, I got buried by RDF up until now.
Comment #47
joelpittetAdding the mailto: and escape bypassing examples from #2545056-11: Attribute class does not sanitize for "javascript:alert('XSS')"
Comment #48
joelpittetWrong assertion it should just strip the protocol not the value.
Comment #52
catchAdded AttributeSafeStringInterface and AttributeSafeString per #46. Docs need lots of work.
I'm not sure we want to use this approach, but it shows there's a way to get the responsive image tests green, while being able to do a more specific check than just SafeStringInterface here.
interdiff is a bit messed up, but has everything except the new files, and includes joelpittet's changes.
Comment #57
catchFixes the theme functions test.
Not sure what's up with the views grid style yet, guessing it's style attribute somewhere.
Comment #58
catchErr wrong patch.
Comment #60
catchTwice :(
Comment #63
dawehnerNeeds some doc updates
Wait, so does that actually mean that Xss::attributes() is wrong at the moment?
Ah yeah, see #2544110: XSS attribute filtering is inconsistent and strips valid attributes
Comment #65
mpdonadio#63-2, one early idea on #2544110: XSS attribute filtering is inconsistent and strips valid attributes was to add setter/getters for its internal lists. If we do that, then Attributes could get the whitelist that way.
Comment #70
wim leers#52 lost the changes I made in #41.
But @dawehner asked in #42 to do that in a separate issue. So here we go: #2569371: Update Renderer::createPlaceholder() to not use Attribute and SafeMarkup::format(). That's an easy patch to get in.
Comment #71
mpdonadioThis needs a reroll, but I am not sure which hunk for the callback attribute name is correct here.
Comment #72
wim leers#71: just
callback, #2569371: Update Renderer::createPlaceholder() to not use Attribute and SafeMarkup::format() removed the need here to prefix it withdata-.Done.
(This is a straight reroll with just the conflict with #2569371: Update Renderer::createPlaceholder() to not use Attribute and SafeMarkup::format() resolved, making this patch slightly simpler.)
Comment #75
catchI've opened #2569485: Add AttributeSafeStringInterface and UriAttributeSafeStringInterface for the interface changes from here.
That may help with other issues in the queue (including critical SafeMarkup ones), so I think we should postpone this on that issue. I haven't moved the interface changes over there yet although all I did was copy and paste SafeString and SafeStringInterface and change a few lines, so we can either hack those bits out of the patch here or start from scratch there.
While this issue would be postponed, the attribute list is also being dealt with in #2544110: XSS attribute filtering is inconsistent and strips valid attributes and that's the only remaining tricky thing in this issue. So if we focus efforts there too, then this patch ends up being a few lines in AttributeString and maybe some conversions - which is what I was hoping it would be when I opened it anyway...
Comment #76
catchComment #77
mgiffordComment #78
wim leersThis is blocked on #2569485: Add AttributeSafeStringInterface and UriAttributeSafeStringInterface, which was in turn blocked on #2576533: Rename SafeStringInterface to MarkupInterface and move related classes. The latter has landed, so we're still blocked on one issue here.
Comment #79
catchI think #2576533: Rename SafeStringInterface to MarkupInterface and move related classes covered what we needed for this issue (in a different way to the initial patches here but a better way), so unpostponing.
Comment #80
catchComment #96
prudloff commentedI agree this is important and would help prevent some XSS vulnerabilities.
I rebased the latest patch and adjusted some things to make tests pass.
However, I see two potential problems with the solution:
This is an existing problem with the XSS filter, but filtering attributes everywhere makes it more visible.
(When this was proposed the interface was still called SafeStringInterface.)
Also filtering the style attribute would break too many places in core where it is used (because every CSS rule contains the : character) and I don't think it is the job stripDangerousProtocols() to remove dangerous URLs in CSS.
Removing dangerous CSS could be implemented later in #3264121: Allow inline style to certain html elements despite of "Limit allowed HTML tags and correct faulty HTML" filter turned on .
Comment #97
needs-review-queue-bot commentedThe Needs Review Queue Bot tested this issue. It no longer applies to Drupal core. Therefore, this issue status is now "Needs work".
This does not mean that the patch necessarily needs to be re-rolled or the MR rebased. Read the Issue Summary, the issue tags and the latest discussion here to determine what needs to be done.
Consult the Drupal Contributor Guide to find step-by-step guides for working with issues.
Comment #98
prudloff commentedI rebased the MR.
Comment #99
joelpittetIt would be so nice if the tests didn’t change. Are the test changes really necessary? Adding more expected assertions is fine, but changing the provider values reduces confidence in the change.
Thanks for picking this up, it's been sitting for a while.
Comment #100
prudloff commentedThe tests have to change (well at least the expected values) because attribute values that were not sanitized before now have their protocol removed.
However, I adjusted the MR a bit to limit the changes in tests.
Comment #101
needs-review-queue-bot commentedThe Needs Review Queue Bot tested this issue. It no longer applies to Drupal core. Therefore, this issue status is now "Needs work".
This does not mean that the patch necessarily needs to be re-rolled or the MR rebased. Read the Issue Summary, the issue tags and the latest discussion here to determine what needs to be done.
Consult the Drupal Contributor Guide to find step-by-step guides for working with issues.
Comment #102
prudloff commentedI merged the latest 11.x.
Comment #103
xjmRetitling because I keep forgetting what this is.
Comment #104
needs-review-queue-bot commentedThe Needs Review Queue Bot tested this issue. It no longer applies to Drupal core. Therefore, this issue status is now "Needs work".
This does not mean that the patch necessarily needs to be re-rolled or the MR rebased. Read the Issue Summary, the issue tags and the latest discussion here to determine what needs to be done.
Consult the Drupal Contributor Guide to find step-by-step guides for working with issues.
Comment #105
prudloff commentedI merged the latest 11.x.
Comment #106
needs-review-queue-bot commentedThe Needs Review Queue Bot tested this issue. It no longer applies to Drupal core. Therefore, this issue status is now "Needs work".
This does not mean that the patch necessarily needs to be re-rolled or the MR rebased. Read the Issue Summary, the issue tags and the latest discussion here to determine what needs to be done.
Consult the Drupal Contributor Guide to find step-by-step guides for working with issues.
Comment #107
prudloff commentedI merged the latest 11.x.
Comment #108
xjmComment #109
smustgrave commentedSo this seems to be toughing a number of different modules and components. Should this be broken up?
Comment #110
needs-review-queue-bot commentedThe Needs Review Queue Bot tested this issue. It no longer applies to Drupal core. Therefore, this issue status is now "Needs work".
This does not mean that the patch necessarily needs to be re-rolled or the MR rebased. Read the Issue Summary, the issue tags and the latest discussion here to determine what needs to be done.
Consult the Drupal Contributor Guide to find step-by-step guides for working with issues.
Comment #111
prudloff commentedI merged the latest 11.x.
Most of the changes are because these parts of the code add an attribute with a dangerous protocol and don't expect it to be filtered, so now they need to wrap the attribute in a Markup object to explicitly prevent it from being filtered.
I guess theses changes could be committed separately but they won't do anything or make a lot of sense on their own.
Comment #112
smustgrave commentedOk, may take a while to get to then just an fyi
Comment #113
needs-review-queue-bot commentedThe Needs Review Queue Bot tested this issue. It no longer applies to Drupal core. Therefore, this issue status is now "Needs work".
This does not mean that the patch necessarily needs to be re-rolled or the MR rebased. Read the Issue Summary, the issue tags and the latest discussion here to determine what needs to be done.
Consult the Drupal Contributor Guide to find step-by-step guides for working with issues.
Comment #114
prudloff commentedI merged the changes from #3537801: Fatal error when using a Twig defined variable as Attribute value.
Comment #115
dcam commentedI left a bunch of comments on the MR. The issue summary needs to be updated too because the proposed resolution only refers to the title, alt, and data-* attributes. The MR excludes a lot more than that from protocol filtering. Please be certain to mention the other issue from which the current list of excluded attributes was taken.
Comment #116
prudloff commentedI applied the suggestions.
Comment #117
prudloff commentedComment #118
dcam commentedUnfortunately, the new function short description doesn't comply with the documentation standards. I left a suggestion for it, along with a couple of other things I noticed while reading through the MR again. I'm sorry I didn't notice those before.
Comment #120
prudloff commentedI applied the suggestions.
Comment #121
needs-review-queue-bot commentedThe Needs Review Queue Bot tested this issue. It fails the Drupal core commit checks. Therefore, this issue status is now "Needs work".
This does not mean that the patch necessarily needs to be re-rolled or the MR rebased. Read the Issue Summary, the issue tags and the latest discussion here to determine what needs to be done.
Consult the Drupal Contributor Guide to find step-by-step guides for working with issues.
Comment #122
prudloff commentedI merged the latest main.