4 ms·
This is a good approach for many apps, but watch out for the thorny XSS issues you can have when mixing server-side rendering with a client-side framework which
by jordanthoms 8y ago
This is a good approach for many apps, but watch out for the thorny XSS issues you can have when mixing server-side rendering with a client-side framework which supports interpolations e.g {{ some_var }} .
Rails/Django etc will correctly sanitize the rendered data for a HTML context, but they don't know that your client-side framework will execute code inside a {{ }} block, so those aren't removed - so if you render something in rails inside a div which is later on part of a Vue or Angular app, you'll have a problem.
There's a few ways around this, e.g. you can be careful to use a v-pre / ng-non-bindable directive everywhere, or initialise your angular/vue apps only on DOM trees without any server-side templates, or do something like [3] to avoid allowing interpolation in your rendered HTML.
1 - https://github.com/dotboris/vuejs-serverside-template-xss https://github.com/dotboris/vuejs-serverside-template-xss
2 - https://github.com/angular/angular.js/issues/5601 https://github.com/angular/angular.js/issues/5601
3 - https://github.com/dotboris/vuejs-serverside-template-xss/issues/6 https://github.com/dotboris/vuejs-serverside-template-xss/is...
- y4mi 8y ago> but they don't know that your client-side framework will execute code inside a {{ }} block Isn't that why they added a verbatim tag?
- lewiscollard 8y agoI think that you might be misunderstanding the problem perhaps because Vue uses a similar curly syntax to output values as Django does (alternatively, I'm misunderstanding you). Consider this method of mounting Vue: new Vue(...config...).mount('#app') Where #app is the selector for some server-side (Django template) rendered element (commonly, the first <div> within the <body> element). _This turns this entire element into a Vue template_. Now, consider your Django template has something like this to echo a comment by a user: y4ml says: {{ comment }} If "comment" in your template context contains Vue curlies, it will be interpreted as such by Vue. So if you wanted to be annoying, your comment could contain: Hi guys, I just wanted to say {{ $&^%£&£%^% }} Which would cause an exception during rendering (a syntax error) and cause your entire #app element to render blank. It doesn't just have possibilities for annoyance, because everything inside those curlies is actually _scope-limited Javascript execution_. Consider this in your Django template: Search results for "{{ request.GET.q }}" Now, if 'q' in your GET contained variable contained: {{ constructor.constructor(alert('hello y4ml')) }} e.g.: /search/?q=%7B%7B%20constructor.constructor%28alert%28%27hello%20y4ml%27%29%29%20%7D%7D You've just created a nasty XSS. Basically, if you're going to mix server-side and client-side rendering you must either ensure that curlies in user-supplied input are always HTML-escaped on output (DON'T do this, you're guaranteed to miss one), or you ensure that user-supplied input is never output in a Django template within an element on which Vue is mounted. The best way of ensuring the latter is to only mount it selectively where it is required, and where it's easy to validate either by eyeball or machine that no user-supplied input will be present (i.e. a 'js-VueMount' class that must ONLY contain one custom element). Does that clarify it, or did I misunderstand your point? (edits: missing curlies, wording clarifications)
- y4mi 8y agoi still dont see the added security issue. you define your template inside {% verbatim %} and if you want to preseed your data, you put that into the new {{ variable|json_script }}... or do you mean that the developer uses a js framework for the main data and keeps using django templates for other, user generated parts (i.e. comments)? that would be a disaster, i agree btw, the last letter is an 'i' :)
- lewiscollard 8y ago> btw, the last letter is an 'i' :) I should increase my font size on HN, sorry about that. :)
- marcus_holmes 8y agoI use go templating to assemble Vue components. Luckily Go templates can switch from using {{ }} to any other delimiter. I use [[ ]]. It works well. And that means I strip all "{{", "}}", "[[" and "]]" from any user input.
- alain_gilbert 8y agoYou can also change vuejs delimiters. var app = new Vue({ delimiters: ['${', '}'], ... });
- kakoni 8y agoInteresting. Do you happen to have examples of these templates?
- jypepin 8y agoI'm not sure how angular works and pretty noob with Vue but IIRC yeah Vue works in a way that it modifies the existing template, so indeed I agree that's a recipe for disaster in this case. With React (at least the way I use it in those cases) is that my main Rails template only renders an empty div container with props for React to render in it. So my full rendering is handled by React, so there is no mix up between Rails and React with data/rendering.