1

I'm looking for most elegant way of "dumping" simple JavaScript object into JavaScript source-code generated on-fly.

Purpose: Assume we have node.js server generating HTML. We have an object x on server side. The object is simple - only strings/ints/arrays in it (so, it's JSON-able). I want to "embed" object x into HTML being generated, to be available for JavaScript code which will run on the browser. So the code:

console.log(x);

will dump exactly the same data on both server-side and browser-side. For example - imagine I'm going to pass some additional config/data to JavaScript running on browser.

Obvious solutions:

  1. Encoding as JSON and send as AJAX/Websocket is not a part of this question as we have to embed the object in the HTML. I don't want additional HTTP requests - everything should be passed in one go.
  2. Encoding as JSON and simply attach to variable sounds initially good, but involves some additional escaping steps.

  3. Using util.inspect() works for me, in this way:

    var toHtml = 'var x = ' + util.inspect(theXonServer, {depth:9}) + ';';

but I'm not sure if it's "elegant" (and secure and error-prone and...)

Any better suggestions ? Standard way of doing that ?

Roman Pietrzak
  • 635
  • 3
  • 15
  • 2
    What is wrong with creating a script element in your html and writing the writing the javascript object there assigning it to a local variable? I say just go with your option 2 and call it done. – bhspencer May 13 '15 at 13:39
  • 2
    @bhspencer, that would seem like a good idea, but makes you open to XSS if any strings contain ``. Safer to add the contents to a `[data-*]` attribute because you can go from object → json encoded object → html escaped json encoded object. Then you have the advantage of being able to use the [dataset api](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/dataset). – zzzzBov May 13 '15 at 13:47
  • Another suggestion made in this post http://stackoverflow.com/questions/23740548/how-to-pass-variables-and-data-from-php-to-javascript/23741119#23741119 is to place the json in a cookie and then retrieve the data from the cookie with client side JS. This avoids the need to fiddle with the html and also avoids the XSS issue. Apparently stackoverflow uses this approach in some places. – bhspencer May 13 '15 at 14:06
  • @bhspencer, some issues with adding it to a cookie are that it'll get passed with every request, which might not be desirable. It also means that you might end up with race conditions if you request the same page in the same browser multiple times. It can work, but be aware of the issues. – zzzzBov May 13 '15 at 14:11

2 Answers2

2

The Wrong Way to Pass Data

It's common to get advice to just stringify some JSON and dump it into a <script> tag. This is bad advice. Don't do it.

It's important to understand why this is a bad idea.

When you string-build JavaScript, you're opening yourself up to all sorts of quirks of the language that you'd absolutely be required to understand to make sure that there are no issues.

One such quirk is that within a <script> element, the first occurrence of </script> will close the <script> element. It doesn't matter that it's in a string, the script will be closed, and the rest of the contents after that point will be treated as HTML.

HTML escaping doesn't work because JS doesn't like HTML entities.

what might start as:

<script>
    window.xss = <%= JSON HERE %>
</script>

could turn into:

<script>
    window.xss = {"username":"Robert Hackerman</script><script src='nefarious.js'></script>"}
</script>

Don't risk it.

The Right Way to Pass Data...

...When the Page is Rendering

The much safer way that prevents any script execution is via [data-*] attributes. You must HTML-escape the contents, but that's OK in attributes. I'm using a <script> element because it's implied that the script will be using the data.

What would start as:

<script data-foo="<%= HTML ENCODED JSON HERE %>" src="yourscript.js"></script>

Would turn into:

<script data-foo="{&quot;username&quot;:&quot;Robert Hackerman&lt;/script&gt;&lt;script src=&apos;nefarious.js&apos;&gt;&lt;/script&gt;&quot;}" src="yourscript.js"></script>

And if you want access to that data, you can just access the attribute value, or use the dataset api (if your target browsers support it):

var fooElement = document.querySelector('[data-foo]');
var rawData = fooElement.dataset.foo;
// or
var rawData = fooElement.getAttribute('data-foo');
var data = JSON.parse(rawData);
console.log(data);

...After the Page has Rendered

If the page has already loaded, and you want to access some data, just use an AJAX request. You'll be able to safely read in a JSON data source, which can be piped through JSON.parse to access the data object.

zzzzBov
  • 174,988
  • 54
  • 320
  • 367
  • Why wouldn't you be able to escape things properly in a ` – Bergi May 13 '15 at 14:24
  • @Bergi, because no language has an "escape for JavaScript" lib function that's been vetted, which means you'd end up writing your own. I can't recommend that approach because it'd be easy to make mistakes. I *can* suggest using the lib functions that most languages have: JSON stringify and HTML escape. The code is easy to read, it's easy to reason about, it's easy to write, and it's **safe**. – zzzzBov May 13 '15 at 14:37
0

Util.inspect vs JSON.stringify

You only need util.inspect if your object is circular. If it's JSON encodable in 99.9% of cases you can just output it to the source with JSON.stringify.

Using JSON

There are edge cases to this - not only are JS objects more expressive than JSON (functions etc), JSON objects can do things JS objects can't (in edge cases of encoding). So make sure not only is your object serializable correctly, it's also deserializable correctly. I also assume you didn't do anything crazy like override the array constructor (which would make JS objects behave differently from JSON ones).

Security

As for security, unless your object can contain sensitive data (and it really shouldn't, whitelist it first) there should not be any related issues.

Overall option 2 is a standard approach that is quite commonly used - including on this very site.

  • It usually works for simple data which is most data you need to share (numbers and strings).
  • It saves the round trip.
  • It's used very often in big sites and in practice.
Community
  • 1
  • 1
Benjamin Gruenbaum
  • 270,886
  • 87
  • 504
  • 504
  • There is also [this question](http://stackoverflow.com/questions/23740548/how-to-pass-variables-and-data-from-php-to-javascript/23741119#23741119) which is very similar about PHP. – Benjamin Gruenbaum May 13 '15 at 13:50