Reputation: 1450
Using the DomSanitizer service in Angular 2+ is it possible to sanitize the html but leave in the css.
For example this :
<p style="text-align: center;">
<span style="color: #e03e2d;">Hello</span>
</p>
<script src="https://nefarious.com/script.js">
Would become this :
<p style="text-align: center;">
<span style="color: #e03e2d;">Hello</span>
</p>
Rather than this :
<p>
<span>Hello</span>
</p>
To give some context, I want to save the content of a TinyMCE editor.
Upvotes: 5
Views: 6270
Reputation: 145950
I got super annoyed about this one and ended up... (judge me if you want) doing this...
First of all my needs:
[style.color]="..."
or even elementRef.style.color = ...
Angular's sanitizer basically has a white listed set of attributes that are allowed to sail through the sanitizer. style="..."
is not one of them. Here are the bulk of them that are allowed (I didn't include ARIA_ATTRS as there are so many).
const URI_ATTRS = tagSet('background,cite,href,itemtype,longdesc,poster,src,xlink:href');
const SRCSET_ATTRS = tagSet('srcset');
const HTML_ATTRS = tagSet('abbr,accesskey,align,alt,autoplay,axis,bgcolor,border,cellpadding,cellspacing,class,clear,color,cols,colspan,' + 'compact,controls,coords,datetime,default,dir,download,face,headers,height,hidden,hreflang,hspace,' + 'ismap,itemscope,itemprop,kind,label,lang,language,loop,media,muted,nohref,nowrap,open,preload,rel,rev,role,rows,rowspan,rules,' + 'scope,scrolling,shape,size,sizes,span,srclang,start,summary,tabindex,target,title,translate,type,usemap,' +
'valign,value,vspace,width');
const VALID_ATTRS = merge(URI_ATTRS, SRCSET_ATTRS, HTML_ATTRS, ARIA_ATTRS);
So anything here is fine and won't get stripped.
So what did I do? In my custom markdown functions I set itemprop
instead of style
and then after sanitizing (and it's very important to sanitize any user content to verify there are no 'script' tags etc.) I just replace itemprop=
with style=
. These styles are all 100% safe from XSS since I made them myself. The alternative of disabling sanitization for all markdown wasn't a good option (since it would allow script
through).
// the ngx-markdown internally calls the Angular sanitizer
// this can only be disabled globally, which wasn't an option
let html = this.markdownService.compile(markdown, ....);
// (not shown) I have a custom markdown renderer which adds itemprop= attributes
// https://marked.js.org/using_pro#renderer
// replace itemprop= with style=
const markdown_with_style = encapsulated.replace(/ itemprop=\"/g, ' style="');
// mark the string as bypassed before applying it with innerHTML
return this.sanitizer.sanitize(SecurityContext.HTML, this.sanitizer.bypassSecurityTrustHtml(markdown_with_style));
I'm completely fine with this approach in this limited scope. The only better approach I can think of would be if the Angular sanitizer would let me mark style
as safe when sanitizing an HTML string.
Final note: If you can abstract your styles into classes then the class
attribute can safely be added - it's only style
that is forbidden.
Upvotes: 3
Reputation: 657
Problem with your solution is if you want style attributes then you'd had to allow CSS in general which is not XSS proof and therefore DomSanitizer.sanitize(...)
is cutting out everything that could lead to a XSS.
If you really need your HTML to show the style attributes then use bypassSecurityTrustHtml(value: string)
instead! But be carefull this will also allow <script>
Tags aswell.
It can be considered safe if TinyMCE is used by only you, trusted users, trusted stakeholders, trusted vip's or moderators that would provide the content with TinyMCE. But if your TinyMCE is thought for your userbase then I'd strongly recommend to NOT use TinyMCE for this approach and rather look out for another solution.
For example you could use something with bbcode functionality like the ckeditor for angular.
Happy coding :)
Upvotes: 4