4

The situation is this: I have some JavaScript that creates an interactive dialogue (and displays it in the form of a comic strip via dynamic element creation... it's fun). The page allows the user to adjust settings, altering the dialogue produced by the script. As a theoretical example, one of the check boxes could be "Use cussing", and the script only adds the cuss words to the returned string if this box is checked. oK, simple enough. Fun.

It currently uses just one template (the "comic strip" has a very specific and pointed purpose) to generate the dialogue; this template is hard coded into the JavaScript. I would like the user to be able to create their own template, which is then turned into a script that is executed just like the hard-coded version, and also able to be controlled by the checkbox options. Edit: To clarify, the user doesn't write script directly. They write a representation (for which I'm now considering using JSON), and when someone else opens the page with the template, the page will THEN turn it into a script and execute it.

This presents some problems. Most obvious is attack by malicious user. Edit: While the user won't be writing a script themselves, since everything's client-side they can easily see my parsing script and may be able to figure out how to insert something. Not really too worried about this, though. End edit. I'm sure there are some standard ways to deal with that, and as currently there's no interaction server-side - everything's client-side - my main concern is that such a user cannot hurt the experience of another user by sharing his bad version. Right now, I'm simply stripping the string at the outset by globally replacing all characters not within a set:

str.replace(/[^a-zA-Z...]/g,'').

Another problem is how to serve their template to the page for others to enjoy. As I said, there's no server-side interaction, and I'd like to keep it that way. I don't want to create MySQL tables and such to store their templates. Unfortunately, this means that the only viable method I can think of is by having their template in the query string of the URL. Nasty, that. And that does impose limits on character length - as little as 2,000 to be compatible with older IE, if I've read right. (I also thought of generating the text of an HTML file they could copy n paste, save as such, host somewhere, and send the address to my page in the URL, which is then loaded into an iFrame...... Ha. Yeah. Not as user-friendly - involves both saving as a file type they've probably never saved as before as well as figuring out how to host a file - and creates even bigger security issues.)

Third problem is once the page has received the template from them, made it secure, and transformed it into a script, how should I execute that script? I've heard since I first started Javascript that eval is evil, so I have avoided using it. Apparently doing new Function(txt) is just as evil (though I have done that many times before). I'm willing to accept these claims of evilness..... But what alternatives are there?? I searched Google for that, and the closest to my situation I could find was this StackOverflow question, involving remote code. The accepted answer proposes dynamically creating a script tag with the textContent set to the script string. Would this also be the best solution for me?

Summary

I need to access user-generated text (preferably without storing it server-side), parse it into Javascript while avoiding attacks, and then execute it with some alternative to eval().

  1. Is storing their template as a query string in the URL, which can then be shared, the only way to get the template to the page?
  2. What are the security holes I should be aware of, and what are the standard ways of protecting against malicious users?
  3. What alternatives to eval are there for executing code that isn't hard-coded into the script, retrieved remotely?
Community
  • 1
  • 1
Marcus Hughes
  • 1,147
  • 2
  • 14
  • 21
  • Even IE9 has a ~2047 character URI (e.g. GET) limit. –  Jun 01 '12 at 20:59
  • 3
    Instead of storing a "script", store something declarative -- e.g. JSON representing the template/actions -- and then apply that only executing "trusted" code. However, consider that jsfiddle.net runs "untrusted code", so.. (this issue, as noted, is in the "impersonation"-style attacks, which are thankfully better guarded than they used to be) –  Jun 01 '12 at 21:00
  • Bundling three questions into one is not how SO is meant to be used. The question will be more useful to others if you break it up. You can make each question shorter and even refer to previous questions – Ruan Mendes Jun 01 '12 at 21:36
  • @JuanMendes: Sorry for the multiple questions - I didn't even think of that. Do you think I should edit it to the one question mostly focused on at this time, and put the other questions in new posts? The reason I did them all as one is that I realize sometimes answers can be situational. What may be suggested for a particular aspect in one situation may not be suggested for that same aspect in another situation. I thought it best to provide full context. – Marcus Hughes Jun 01 '12 at 22:10
  • I don't think rewriting now is necessary, just for future reference – Ruan Mendes Jun 01 '12 at 22:12
  • @pst: I didn't actually store a script. The user creates a more human readable representation, which is passed in the URL, interpreted by the script on each user's end, and only then turned into a script and executed. I'll edit my post to make this clearer. However, I didn't think of using JSON. Thanks for the suggestion. – Marcus Hughes Jun 01 '12 at 22:13

1 Answers1

3

As @pst said, use a JSON representation of template features (and arguments), and only run trusted code.

Maybe something like

{
  border: { color: '#BADA55' },
  layout: { columns: 2, rows: 4 }
}

and then just parse it with a loop, an array of handlers, a switch...case cascade, or similar.

Compacted a bit, this could fit a pretty detailed template in under 2000 characters.


jsfiddle safely runs untrusted code by serving it in an iframe from a different domain name, so if this is an option and you really want to eval some untrusted code, just do that. You'll still end up making a declarative representation (or trying to sanitize it perfectly until you realize you can't) if you insist on preventing "hurt experiences".

bkconrad
  • 2,620
  • 3
  • 20
  • 30
  • This is it, don't let the user create scripts, let them create data (JSON) and you properly execute it making sure you don't eval anything. – Ruan Mendes Jun 01 '12 at 21:13
  • 2
    I don't think jsfiddle is a good comparison. At JS fiddle, you must allow the user to enter code. In the OP's case, there's no need for the user to enter actual code – Ruan Mendes Jun 01 '12 at 21:14
  • I agree. I was just pre-empting a commonly offered example of "safe eval'ing" – bkconrad Jun 01 '12 at 21:16
  • But it's very true, the safeboxing technique you mentioned does allow you to safely run untrusted code if the OP desires to do that, would give you another upvote if I could – Ruan Mendes Jun 01 '12 at 21:31
  • I think the representation via JSON idea is great - I was doing something similar, but this is probably better. But I'm still not sure how you're suggesting I actually execute it. I have to admit I'm not familiar with JSON - never looked at it. So that may be a silly question. But as I parse the JSON, am I still going to be using eval, or what? – Marcus Hughes Jun 01 '12 at 22:19
  • There are a handful of JSON parsers, I usually use jQuery's `jQuery.parseJSON()`. Although it's a cool feature of JSON that it can be eval'd, it's much better just to use a parser. – bkconrad Jun 01 '12 at 22:21
  • Also, you say I CAN compact it into the URL. But is that the best, or should I really consider storing it server-side? It IS an option, just not one I wanted to bother with. – Marcus Hughes Jun 01 '12 at 22:22
  • Then you definitely 100% should store it server-side. Something like this should be very straightforward. – bkconrad Jun 01 '12 at 22:23