Brad Parks
Brad Parks

Reputation: 72001

Force Backbone or Underscore to always escape all variables?

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

Answers (3)

Brad Parks
Brad Parks

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>

Making this work globally

To get this to work globally, you may have to configure Underscore as a module, which can be done like so:

Implement as a dependency on Backbone

Configure Require.js modules

// 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'
        }
    }
});

underscoreConfig.js

define(['underscore'], function (_) {
    'use strict';

    _.templateSettings = 
    {
      escape: /<%[=-]([\s\S]+?)%>/g,
      interpolate: /<%cleanHtml([\s\S]+?)cleanHtml%>/g,
      evaluate: /<%([\s\S]+?)%>/g
    };

    return _;
});

Upvotes: 4

ken.dunnington
ken.dunnington

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

inf3rno
inf3rno

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

Related Questions