Reputation: 2807
I'm working on a website where there is going to be a lot of user generated content. As an WYSIWYG editor I'm using tinyMCE. As a template engine, I'm using ejs. In order to prevent XSS I decided to use xss npm package. I'm using these custom rulles:
const strict = {
whiteList: {},
}
const withTags = {
whiteList: {
div: [],
strong: [],
em: [],
br: [],
ul: [],
li: [],
ol: [],
blockquote: [],
},
}
Let's say a user uses text area and submits this code:
<script>alert("hi")</script>
In my DB it's saved this way:
<script>alert("hi")</script>
Now, when the content is rendered by ejs some content is rendered with escaped output (<%= %>). Rendered escaped output (html page) looks like this:
<script>console.log("test")</script>
However, some content is rendered with ejs unescaped raw output (<%- %>). Html will look this way:
<script>console.log("test")</script>
I have 2 questions:
<
and >
is it safe to render ejs unescaped output?Thank you very much!
Upvotes: 0
Views: 423
Reputation:
First of all your data probably isn't saved in database the way you presented. In all likelihood it's stored there without any encoding (as it should be).
EJS in itself, when used correctly, takes care of encoding output for you so that you can safely construct parameterized HTML. But in your case you want to disable this protection to render raw HTML, so yes, you must be careful. There are a couple of security controls at your disposal.
1. DOMPurify
I haven't used the xss library personally, it seems to have a lot of downloads and probably it's not a bad option. But DOMPurify is probably better. It also doesn't require configuration and has built-in support for trusted-types (I'll get to that in a minute).
You would use it twice. First on server-side when the HTML is submitted by the user, and second on client-side when the HTML is rendered by EJS.
If you are serious about security then you will connect anomaly alerts from the server-side purification to your SIEM/SOC etc. Then you will know when someone has attempted an XSS attack on your website.
2. Sandboxed Iframes
Another client-side control that you can implement is sandboxed iframes. Instead of just rendering the HTML on the page, you create an IFRAME, give it a properly configured sandbox attribute, and then set the purified HTML as the content. Now even if something goes wrong with the purification, the malicious HTML would be isolated in its own world.
3. Content Security Policy
The coolest and (when used properly) most effective defence against XSS is CSP. How it works is that you give your website restrictions such as "do not execute scripts", "do not load images", etc. And then you allow the scripts that you do want to execute, and nothing else. Now if an attacker manages to inject a script, link, form, etc. on the page, it will not work because it hasn't been specifically allowed.
I've written about CSP at length here, you will even find specific examples for your case (NodeJS and EJS) with CodeSandbox examples on that article. And in general about XSS protection you can read more here.
Hope this helps!
Upvotes: 3