Reputation: 72001
I'm new to Backbone, and am helping maintain an app. I'd like to make the default in all normal situations for Backbone to be escaping model data, to help avoid XSS attacks by default.
I know we can do this using
<%- someModelAttribute %>
and
model.escape('attr')
to escape data in our app, but I'd like to switch it so
<%= someModelAttribute %>
and
model.get('attr')
did the same as well.... So by default, all existing code and future code that uses these tags and methods is escaped by default. Then I'd like to introduce another model method like "model.getDataThatShouldBeSafeHtml" to make it 100% clear to developers when they're getting data that should include HTML.
So is there some way for me to switch the "<%=" tag and the "model.get" methods to be the same as their escape equivalents?
I only ask as I thought this might've been done somewhere before, or already be part of backbone, and I want to avoid rebuilding the wheel!
Upvotes: 4
Views: 2332
Reputation: 72001
I found an easy way to make it so all templates by default are escaped. Since Backbone uses Underscore for it's templating engine, I googled around and found that you can customize the underscore delimiters using _.templateSettings
, described here on the Underscore site. Note that if you make sure all of your html is written out using these templates, then you're covered for all XSS. So don't skip using templates in some simple scenarios, use them to avoid XSS in those cases as well!
You can test it out using this fiddle: http://jsfiddle.net/vx0pw2n0/
So all I did was make it so both <%=
and <%-
are used to display escaped data by default. The evaluate tag <%
still remains, and can be used to output whatever HTML you want using the print
statement. I also introduced a new tag, <%cleanHtml
that can be used to output HTML without it being escaped, and without needing to say print(someVariable)
<script type="text/javascript">
//
// This is the important part - The part that changes what underscore uses
// for template delimiters.
//
_.templateSettings =
{
escape: /<%[=-]([\s\S]+?)%>/g,
interpolate: /<%cleanHtml([\s\S]+?)cleanHtml%>/g,
evaluate: /<%([\s\S]+?)%>/g
};
// Test it out
var t = _.template($('#t').html());
var html = t({ title: '<b>pancakes</b>' });
$("#target").html(html);
console.log(html);
</script>
<!-- Sample Underscore Template showing different ways of using it -->
<script id="t" type="text/x-underscore">
<div><%= title %></div>
<div><%- title %></div>
<div><%safeHtmlOnly title safeHtmlOnly%></div>
<div><% print(title) %></div>
</script>
<div id="target"></div>
To get this to work globally, you may have to configure Underscore as a module, which can be done like so:
// When you initially setup require.js, add a new module to configure underscore
// Make it a dependency of backbone, so it'll always be loaded whenever
// backbone is used.
require.config({
shim: {
underscore: {
exports: '_'
},
backbone: {
deps: ['underscoreConfig', 'underscore', 'jquery'],
exports: 'Backbone'
},
jquery: {
exports: 'jQuery'
}
}
});
define(['underscore'], function (_) {
'use strict';
_.templateSettings =
{
escape: /<%[=-]([\s\S]+?)%>/g,
interpolate: /<%cleanHtml([\s\S]+?)cleanHtml%>/g,
evaluate: /<%([\s\S]+?)%>/g
};
return _;
});
Upvotes: 4
Reputation: 905
You could extend Backbone.Model
to create a reusable base class that does this for you. Something like this (untested):
BaseModel = Backbone.Model.extend({
getSafeAttributes: function() {
var safe = {};
_.each(this.attributes, function(key, value) {
safe[key] = _.escape(value);
});
return safe;
}
});
I'm guessing your render
functions look something like
this.$el.html(this.template(this.model.attributes));
So, instead of that, you'd write:
this.$el.html(this.template(this.model.getSafeAttributes()));
and just make sure your models extend the base class instead of Backbone.Model
.
It's perfectly acceptable to modify backbone.js
and underscore.js
to achieve a similar outcome, but it does make it a pain to upgrade, which is why I'd go with the base class instead.
Upvotes: 2
Reputation: 26139
As far as I know backbone depends on the template engine provided by underscore.
How to use underscore.js as a template engine?
By underscore you can change the regex patterns to always escape, but it won't call the escape method of your model, so you have to create your own template engine to do that...
For example you can fork the templating code from underscore and develop it in your own way and use that instead of _.template()
.
Upvotes: 0