This issue has been reported privately to the security team but it was decided to handle this as public security improvement since no direct vulnerability is involved.
Problem/Motivation
Watchdog messages should be inserted sanitized into the database, but it is easy for developers to make the mistake of inserting user provided text as log message.
Proposed resolution
We should add an additional safe guard and do Admin XSS filtering when the log message is displayed in the user interface.
Remaining tasks
Write a patch against dblog.module
User interface changes
none.
API changes
none.
Comment | File | Size | Author |
---|---|---|---|
#44 | interdiff-2459339-35-44.txt | 7.51 KB | hgoto |
#44 | xss-dblog-2459339-44-d7.patch | 2.28 KB | hgoto |
#13 | xss-dblog-2459339-13.patch | 2.55 KB | klausi |
Comments
Comment #1
klausiComment #2
idebr CreditAttribution: idebr commentedThe markup for dblog messages is being worked on in #2345779: Fix double-escaping due to Twig autoescape in dblog event "operations" and includes XSS filtering for messages, so this can possible be closed when the related issue is committed.
Comment #4
dagmarThis is already considered and tested here:
http://cgit.drupalcode.org/drupal/tree/core/modules/dblog/src/Tests/DbLo...
Comment #5
gregglesThanks, dagmar. Moving back to 7.x in case anyone wants to backport that change.
Comment #6
David_Rothstein CreditAttribution: David_Rothstein as a volunteer commentedThis is still reproducible in Drupal 8 - just add code somewhere that does
\Drupal::logger('my_module')->notice('<script type="text/javascript">alert("xss");</script>')
and then go to the admin page to view the message.Note that the issue (for both Drupal 7 and 8) only occurs when you click through to the actual log message, not when it's displayed on the overview page at admin/reports/dblog. The test linked above is for admin/reports/dblog so it isn't relevant here.
Comment #7
David_Rothstein CreditAttribution: David_Rothstein as a volunteer commentedI guess I shouldn't say it isn't relevant - it is (and the test is probably worth backporting too). But it's not a complete test for this issue since log messages are displayed elsewhere too.
Comment #8
dagmarThanks @David_Rothstein. I will take this issue.
Comment #9
dagmarHere is the patch.
Comment #10
dagmarActually this could be simplified.
Comment #11
klausiThis is the wrong place to sanitize. In Drupal 8 it is very dangerous to pass dynamic content to t(), so you need to sanitize in the formatMessage() method. Before passing the message to t() you should apply Xss:filterAdmin(), at least that is my assessment. Any other opinions?
Comment #12
dagmar@klausi I agree but, in this case apply the sanitizing function inside formatMessage will double escape the link generation in the listing See http://cgit.drupalcode.org/drupal/tree/core/modules/dblog/src/Controller/DbLogController.php#n182.
Based on this could you reconsider your comment? Or I need to refactor the entire
formatMessage
method?Comment #13
klausiXss::filterAdmin() does not escape characters, it only strips dangerous HTML tags. Therefore no double escaping can happen.
This is my suggestion, patch attached.
Comment #14
dagmarThanks @klausi this works fine and my tests introduced in #10 are confirming this.
Comment #17
catchCommitted/pushed to 8.2.x and cherry-picked to 8.1.x. Thanks!
Comment #18
andypostLooks that needs follow-up and proper status
Why message translated only in second case?
Comment #19
andypostComment #20
hgoto CreditAttribution: hgoto as a volunteer and at Studio Umi commentedFollowing the approach of the patch #13, I created a patch for D7. I'd like someone to review this. Thank you in advance. Here are points:
- I added the
filter_xss_admin()
(D7 version ofXss::filterAdmin()
) to filter the message output.- Also I revised the helper method
generateLogEntries()
in the classDBLogTestCase
to match it with the D8 version and use it in the test case added.@andypost,
> Why message translated only in second case?
I believe that this is because messages without any variable may be very case specific and should not be added into translation texts.
'N;'
equals toserialize(NULL)
.Comment #21
hgoto CreditAttribution: hgoto as a volunteer and at Studio Umi commentedComment #22
kporras07 CreditAttribution: kporras07 at Manatí commentedHi,
I tested the patch in #20 and it works like a charm.
It would be great to get this committed.
Comment #24
David_Rothstein CreditAttribution: David_Rothstein as a volunteer commentedLooks like it probably just needs a simple reroll.
Comment #25
kporras07 CreditAttribution: kporras07 at Manatí commentedReroll patch against last 7.x
Comment #26
David_Rothstein CreditAttribution: David_Rothstein as a volunteer commentedJust a quick look at the patch right now... but:
Does putting the filter_xss_admin() inside of the t() have the potential to break translations (since it can alter the string)?
Because I don't think this (from above) is quite true:
At least testing with Drupal 7:
Results in
test < & >
as output.I think it is smart enough not to double-escape maybe (if it is run twice), but even a single escape like the above looks to me like it might break translations for any string that has those kinds of characters in it.
Comment #27
kporras07 CreditAttribution: kporras07 at Manatí commented@David_Rothstein, initially I had done a quick xss test (when I set the RTBC), then I just rerolled the patch; however, due to your last comment; I did some more testing and it looks to be working as expected: no xss, strings looks ok in WD overview and WD detail.
This is the code that I've used to test:
If you have more advices; I'll be really glad and I'll try to apply them.
Thanks,
Comment #28
David_Rothstein CreditAttribution: David_Rothstein as a volunteer commentedThanks.
What I'd be curious about based on my above comment is if the string in the second example (
'< & > @test'
) has a translation into a different language, what happens? My guess is that before the patch the translated version would appear in the logs (to an administrator viewing the log messages page in a non-English language) but afterwards the non-translated version would appear.Comment #29
hgoto CreditAttribution: hgoto as a volunteer and at Studio Umi commentedI tested the translation with the sample code #27.
The result is that, exactly as David_Rothstein told,
tranlation source texts changed with the patch #25. So #25 is not appropriate.
I tried creating a new patch for D7 to avoid this translation break. The diff is so small.
Again, as for D8, should we create a new patch for D8 or not? The patch #13 was already committed and I created a new one as a starting point. Even with this patch, there can be double escapes for the log list page. Should we fix that now? (This double escape problem is not seen in D7, I believe.)
Comment #32
hgoto CreditAttribution: hgoto as a volunteer and at Studio Umi commentedThe patch #29 for D7 failed to apply the latest dev because other commit changed the same file... I've recreated it and am going to retry it first.
As for D8 patch, my local test was not enough, I'm sorry.
Comment #33
hgoto CreditAttribution: hgoto as a volunteer and at Studio Umi commentedComment #35
hgoto CreditAttribution: hgoto as a volunteer and at Studio Umi commentedI failed to delete one line and adjusted it.
Comment #36
David_Rothstein CreditAttribution: David_Rothstein as a volunteer commentedThe new approach makes more sense to me. It does mean you can't display HTML tags in the logs that filter_xss_admin() doesn't allow (even if you pass that via a
!placeholder
to t()) but that's probably OK. I doubt most people want lots of HTML in their log messages anyway.I just noticed the code right above this (visible in the patch file) is using filter_xss() but this new code is using filter_xss_admin(). We should probably reconcile that (or at least add a code comment)... maybe it makes some sense if the former is going into a link and the latter isn't. Overall I think filter_xss_admin() is better here, to be as permissive as possible.
What is the reason for making all those changes to the generateLogEntries() method in the tests? Since we're just trying to test a specific log message here, I would think having the new test call watchdog() directly would be better.
I guess we should create a separate Drupal 8 issue to possibly fix this as a followup there.
Comment #37
David_Rothstein CreditAttribution: David_Rothstein as a volunteer commentedI created #2760031: Log messages won't appear translated if they have special characters in them as a related issue for fixing that problem in Drupal 8.
Comment #38
hgoto CreditAttribution: hgoto as a volunteer and at Studio Umi commented@David_Rothstein, thank you for your detailed feedback. I created a new patch.
1.
I see. I saw the corresponding D8 code and found that the D8 method
formatMessage()
which replacestheme_dblog_message()
of D7 doesn't add the link. Instead, page callbackDbLogContoller::overview()
add a link after generating the message text fromformatMessage()
method.a code in
DbLogController::overview()
of D8:After seeing this, I'd like to take the same approach as D8 does in D7 if it's acceptable. I mean, I'd like to simplify the
theme_dbglog_mesasge()
to make it create a text withoutlink
option. Then, the complexity intheme_dblog_message()
that usesfilter_xss()
andfilter_xss_admin()
is cleared.But, this change can be considered as an API change (since
theme_dbglog_mesasge()
will not uselink
parameter any more.). Shouldn't we do this?2.
I see. Exactly. I replaced
generateLogEntries()
into a simpler process without calling that method.3.
This point is not a bug and a new proposal. If possible, I'd like to add a title attribute to each link to improve UI in this patch as following:
This is exactly how D8 does.
Here's a revised patch and I'd like it to be reviewed.
Or should we wait for #2760031: Log messages won't appear translated if they have special characters in them to be fixed first before going ahead on this issue?
Comment #40
hgoto CreditAttribution: hgoto as a volunteer and at Studio Umi commentedhmm. There was a new failed test in #38. I may have changed
theme_dblog_message()
too much... I'd like someone to review this approach. Anyway, I fixed the test failure.Comment #41
hgoto CreditAttribution: hgoto as a volunteer and at Studio Umi commentedComment #43
hgoto CreditAttribution: hgoto as a volunteer and at Studio Umi commentedI'm sorry to mess up the thread. I'll fix the point that causes the error.
Comment #44
hgoto CreditAttribution: hgoto as a volunteer and at Studio Umi commentedI went a wrong way between #38 and #43... I'd like to restart from the point #35 and #36.
I updated the patch #35 following David_Rothstein's feedback.
I investigated this point. As David_Rothstein told, the following code strips all the tags from $output and make it a link.
This pattern is used in the dblog overview (list) page where links are attached to the messages.
(
filter_xss($output, array())
filters out all the tags and special characters as the empty second parameterarray()
means there's no allowed tag.)So I added a new comment to explain this above the if statement for link.
I see. Surely we don't need to use
generateLogEntries()
here. I replaced it to a simpler test code. And I stopped to changegenerateLogEntries()
because this change is not necessary any more at this point.As a result, the patch became much simpler. I'd like someone to review this.
Comment #47
Fabianx CreditAttribution: Fabianx as a volunteer and at Tag1 Consulting commentedRTBC - looks good to me!
Comment #48
stefan.r CreditAttribution: stefan.r commentedComment #49
stefan.r CreditAttribution: stefan.r commentedComment #50
stefan.r CreditAttribution: stefan.r commentedRe-reviewed with Fabianx, and committed and pushed to 7.x, thanks!
Comment #53
stefan.r CreditAttribution: stefan.r commentedComment #54
David_Rothstein CreditAttribution: David_Rothstein as a volunteer commented