Backbone.js / Underscore.js:为什么我的模板没有转义

时间:2013-03-20 19:34:54

标签: javascript backbone.js xss underscore.js

使用最新的主干和下划线库(1.0.0 / 1.4.4)我无法弄清楚为什么我的模板没有像我期望的那样转义。这是一个简化版本:

M = Backbone.Model.extend({});
V = Backbone.View.extend({
    template: _.template("<%- attr %>"),
    render: function() {
        this.$el.html(this.template(this.model.attributes));
        return this;
    }
});
m = new M({attr: "<script>bad</script>"});
v = new V({model: m});
v.render();
console.log(v.el);

结果:

<div><script>bad</script></div>
显然,这并没有被转义。我已经验证了作为独立代码执行的模板正确转义:

T = _.template("<%- attr %>");
console.log(T({attr: "<script>bad</script>"}));

产生

的预期结果
&lt;script&gt;bad&lt;&#x2F;script&gt;

所以某个地方的内容没有被转义。如果有人知道在哪里,或者更好的是,如何防止它,我将非常感激。

斯蒂芬


更新

想出来!虽然问题不在于控制台本身,但 mu 的回答确实指出了我正确的方向。为了解释,我需要详细说明上面的示例代码,以便更准确地反映我的上下文。该上下文实际上不是控制台输出,而是单元测试。我想验证我的代码是否正确防范XSS攻击,所以我编写了单元测试(使用mocha / sinon / chai / should):

v.$el.text().should.equal(m.escape("attr"));

该测试失败(意外),而下面的测试正在通过(我认为不应该这样):

v.$el.text().should.equal(m.get("attr"));

罪魁祸首证明是元素对象上的toString()方法。 console.log()使用的方法取消了内容。 jQuery在text()函数中也(间接)使用该方法。

我不确定这在所有浏览器中都是通用的,但在我的情况下使用.innerHTML()可以避免使用toString() unescape。所以编写测试的正确方法是

v.el.innerHTML().should.equal(m.escape("attr"));

1 个答案:

答案 0 :(得分:3)

我认为你的控制台过于“乐于助人”,令你感到困惑。如果我们仔细研究一下,我们就会看到究竟是什么样的诡计;我将切换到<b>而不是<script>,以避免将<script>分成几块以避免混淆jsfiddle。

考虑一下:

M = Backbone.Model.extend({});
V = Backbone.View.extend({
    template: _.template("<%- attr %>"),
    render: function() {
        this.$el.html(this.template(this.model.attributes));
        return this;
    }
});
m = new M({attr: "<b>bad</b>"});
v = new V({model: m});
v.render();

$('body').append(v.el);
$('body').append($(v.el).text()).append('<br>');
$('body').append($(v.el).html());

您会在页面上看到三件事:

  1. 文字<b>bad</b>
  2. 以粗体显示bad
  3. 文字<b>bad</b>
  4. 我们希望看到attr 被转义。

    但是,你正在看控制台。所以,让我们看看控制台,并在上面的JavaScript底部抛出一些东西:

    console.log(v.el);
    console.log(v.el.innerHTML);
    

    现在在控制台中我们看到了:

    <div><b>bad</b></div>
    &lt;b&gt;bad&lt;/b&gt;
    

    但仔细观察第一行,您应该会看到<div></div>出现在紫色(或控制台用于DOM元素的任何高光颜色),而<b>bad</b>为黑色(或者您的控制台用于纯文本的任何内容)。因此,attr正在被正确转义,但您的控制台在显示之前正在解码。

    演示:http://jsfiddle.net/ambiguous/5nYTd/