We don’t actually need $sanitize
to sanitize strings.
ng-bind
automatically implements sanitizing in the background with textContent
. However, sometimes we need to pass string in as template in controllers and services, or pass it in as the exact content of “name
” attribute of an input, then $sanitize
doesn’t work anymore.
What $sanitize
does is that it renders the content as safe html to render with innerHTML, but what it does not do is sanitizing only attributes of elements. Sanitizing HTML and sanitizing textContent/attributes are entirely different things.
See the comparisons here. The string to be sanitized is: "><img src='https://tinyurl.com/ycosred9' onload=prompt('XSS')>
.
From the 3 examples above, we can see that if we try to use $sanitize(msg)
in the controller to sanitize textContent or attribute, what comes out will no longer be the same string any more. For one thing, if we try to use {{ $sanitize(msg) }}
in html, all the ">
still remain. For another, the “onload
” part of the string is gone.
Sanitizing doesn’t need to be so complicated.
It doesn’t even need to involve Angular at all.
Solution
const str = `><img src='https://tinyurl.com/ycosred9' onload=prompt('XSS')>`; const ele = document.createElement("div"); // use setAttribute to set attributes ele.setAttribute("name", str); // use textContent to set the content of the element ele.textContent = str;
By creating an element in JavaScript and using built-in APIs to set attribute and element content, we can leverage browsers’ own sanitize function.
Then, to make use of the element we created:
// 1. Append it to the element we want document.body.appendChild(ele); // 2. Or if we need the string version of the element, use outerHTML to get it const templateString = ele.outerHTML;
Read more about dealing with Cross-Site Scripting here.