I\'m thinking about embedding arbitrary JSON in the DOM like this:
HTML5 includes a <data> element for keeping machine readable data. As a—perhaps safer—alternative to <script type="application/json">
you could include your JSON data inside the value
attribute of that element.
const jsonData = document.querySelector('.json-data');
const data = JSON.parse(jsonData.value);
console.log(data)
<data class="json-data" value='
{
"unicorns": "awesome",
"abc": [1, 2, 3],
"careful": "to escape ' quotes"
}
'></data>
In this case you need to replace all single quotes with '
or with "
if you opt to enclose the value with double quotes. Otherwise your risk XSS attacks like other answers have suggested.
I think your original method is the best. The HTML5 spec even addresses this use:
"When used to include data blocks (as opposed to scripts), the data must be embedded inline, the format of the data must be given using the type attribute, the src attribute must not be specified, and the contents of the script element must conform to the requirements defined for the format used."
Read here: http://dev.w3.org/html5/spec/Overview.html#the-script-element
You've done exactly that. What is not to love? No character encoding as needed with attribute data. You can format it if you want. It's expressive and the intended use is clear. It doesn't feel like a hack (e.g. as using CSS to hide your "carrier" element does). It's perfectly valid.
My recommendation would be to keep JSON data in external .json
files, and then retrieve those files via Ajax. You don't put CSS and JavaScript code onto the web-page (inline), so why would you do it with JSON?
I'd suggest to put JSON into an inline script with a function callback (kind of JSONP):
<script>
someCallback({
"unicorns": "awesome",
"abc": [1, 2, 3]
});
</script>
If the executing script is loaded after the document you can store this somewhere, possibly with an additional identifier argument: someCallback("stuff", { ... });
See Rule #3.1 in OWASP's XSS prevention cheat sheet.
Say you want to include this JSON in HTML:
{
"html": "<script>alert(\"XSS!\");</script>"
}
Create a hidden <div>
in HTML. Next, escape your JSON by encoding unsafe entities (e.g., &, <, >, ", ', and, /) and put it inside the element.
<div id="init_data" style="display:none">
{"html":"<script>alert(\"XSS!\");</script>"}
</div>
Now you can access it by reading the textContent
of the element using JavaScript and parsing it:
var text = document.querySelector('#init_data').textContent;
var json = JSON.parse(text);
console.log(json); // {html: "<script>alert("XSS!");</script>"}
As a general direction, I would try using HTML5 data attributes instead. There's nothing to stop you putting in valid JSON. e.g.:
<div id="mydiv" data-unicorns='{"unicorns":"awesome", "abc":[1,2,3]}' class="hidden"></div>
If you're using jQuery, then retrieving it is as easy as:
var stuff = JSON.parse($('#mydiv').attr('data-unicorns'));