I'm using backbone, and the general way for passing the collections when the page load is
window.router = new Routers.ManageRouter({store: #{@store.to_json});
which is fine and works well, until someone decides to add the text "<script>alert("owned")</script>
" to one of the store fields. the last </script>
obviously closes the javascript. How can this be circumvented?
:javascript
$(function() {
window.router = new Dotz.Routers.ManageRouter({store: #{@store.to_json}});
Backbone.history.start();
});
The above outputs:
<script>
//<![CDATA[
$(function() {
window.router = new Dotz.Routers.ManageRouter({store: '{"_id":"4f3300e19c2ee41d9a00001c", "points_text":"<script>alert(\"hey\");</script>"'});
Backbone.history.start();
});
//]]>
</script>
I'm using backbone, and the general way for passing the collections when the page load is
window.router = new Routers.ManageRouter({store: #{@store.to_json});
which is fine and works well, until someone decides to add the text "<script>alert("owned")</script>
" to one of the store fields. the last </script>
obviously closes the javascript. How can this be circumvented?
:javascript
$(function() {
window.router = new Dotz.Routers.ManageRouter({store: #{@store.to_json}});
Backbone.history.start();
});
The above outputs:
<script>
//<![CDATA[
$(function() {
window.router = new Dotz.Routers.ManageRouter({store: '{"_id":"4f3300e19c2ee41d9a00001c", "points_text":"<script>alert(\"hey\");</script>"'});
Backbone.history.start();
});
//]]>
</script>
Share
Improve this question
edited Feb 12, 2012 at 17:17
Phrogz
303k113 gold badges667 silver badges756 bronze badges
asked Feb 12, 2012 at 4:12
CamelCamelCamelCamelCamelCamel
5,2008 gold badges63 silver badges94 bronze badges
3 Answers
Reset to default 15Inside a <script>
block it is syntactically illegal to have any </
followed by a name—not just </script>
—so you need to escape that anywhere it may appear. For example:
:javascript
var foo = { store: #{@store.to_json.gsub('</','<\/')} };
This will create the sequence <\/
inside your JS strings, which is interpreted to be the same as </
. Ensure that you use single quotes in your gsub replacement string, or else use gsub( "</", "<\\/" )
due to the difference between single and double quotes in Ruby.
Shown in action:
irb:02.0> s = "<b>foo</b>" # Here's a dangerous string
#=> "<b>foo</b>"
irb:03.0> a = [s] # Wrapped in an array, for fun.
#=> ["<b>foo</b>"]
irb:04.0> json = a.to_json.gsub( '</', '<\/' ) # Sanitized
irb:05.0> puts json # This is what would come out in your HTML; safe!
#=> ["<b>foo<\/b>"]
irb:06.0> puts JSON.parse(json).first # Same as the original? Yes! Yay!
#=> <b>foo</b>
If you are using Rails (or ActiveSupport) you can enable JSON escaping:
ActiveSupport::JSON::Encoding.escape_html_entities_in_json = true
Seen in action:
irb:02.0> a = ["<b>foo</b>"]
irb:03.0> puts a.to_json # Without the magic
#=> ["<b>foo</b>"]
irb:04.0> require 'active_support'
irb:05.0> ActiveSupport::JSON::Encoding.escape_html_entities_in_json = true
irb:06.0> puts a.to_json # With the magic
#=> ["\u003Cb\u003Efoo\u003C/b\u003E"]
It produces JSON that is more verbose than you need to solve this particular problem, but it is effective.
The magic word is:
ActiveSupport.escape_html_entities_in_json = true
Although marked as deprecated, this still works in current rails versions (see my rails c
):
ruby-1.9.3-head :001 > ::Rails.version
=> "3.2.1"
ruby-1.9.3-head :002 > ["<>"].to_json
=> "[\"<>\"]"
ruby-1.9.3-head :003 > ActiveSupport.escape_html_entities_in_json = true
=> true
ruby-1.9.3-head :004 > ["<>"].to_json
=> "[\"\\u003C\\u003E\"]"
You forgot the ''
:javascript
$(function() {
window.router = new Dotz.Routers.ManageRouter({store: '#{@store.to_json}'});
Backbone.history.start();
});