Javascript eval (and friends)
Asked Answered
M

5

8

Some claim eval is evil.

Any regular HTML page may look like:

        <script src="some-trendy-js-library.js"></script>
    </body>
</html>

That is, assuming the person doing this knows his job and leaves javascript to load at the end of the page.

Here, we are basically loading a script file into the web browser. Some people have gone deeper and use this as a way to communicate with a 3rd party server...

<script src="//foo.com/bar.js"></script>

At this point, it's been found important to actually load those scripts conditionally at runtime, for whatever reason.

What is my point? While the mechanics differ, we're doing the same thing...executing a piece of plain text as code - aka eval().


Now that I've made my point clear, here goes the question...

Given certain conditions, such as an AJAX request, or (more interestingly) a websocket connection, what is the best way to execute a response from the server?

Here's a couple to get you thinking...

  • eval() the server's output. (did that guy over there just faint?)
  • run a named function returned by the server: var resp = sock.msg; myObj[resp]();
  • build my own parser to figure out what the server is trying to tell me without messing with the javascript directly.
Meristic answered 13/2, 2012 at 19:10 Comment(12)
Your third option isn't really an option for executing a response from the server, just processing it. If you're going to include that option, it would seem reasonable to include JSON and XML as options as well.Dollhouse
@T.J.Crowder - But JSON and XML are just the transport format. I've seen people return functions (code) as strings (maximum compatibility) and others embedding functions in JSON (which only works with eval-ing JSON, and not the browser's own method). The third option would simply be an over-engineered effort of interpreting a language within an interpreted language. :/Meristic
I'd prefer to see you NOT execute code from a server, but rather write code which reads the result from the server and does things based on that. Otherwise you are opening yourself up to serious security concerns.Ximenez
@Christian: Right, but your third option is the same thing: Writing a parser to figure out what the server has sent you. Basically no different from parsing JSON, XML, or any other text format, unless you're going to re-implement JavaScript in full. (Ans you cannot embed functions in JSON; people who do are no longer using JSON, they're using JavaScript.)Dollhouse
@Ximenez - What makes you think the code I returned from the server in the first place was secure?Meristic
@T.J.Crowder The re-implementing javascript was exactly my point. I've been a bit rhetorical with that one.Meristic
That's not a very good approach to security. Your basically suggesting, "Well if it's been compromised already in one aspect, why bother taking precautions elsewhere".Ximenez
Is it the purpose of your question to make the point that a script request is no safer than eval() when requesting content you don't control?Linker
@Ximenez The web is fundamentally flawed...that's why server whiz code at you. I can't do much about it than starting to ship desktop apps instead of websites. Not much of an option is it?Meristic
@amnotiam - That was a disclaimer so I wouldn't get the kind of people shouting "you're clueless" at me.Meristic
Your question seemed to be written from the perspective of executing a server response, which was requested from javascript. AJAX and websockets both certainly imply that. If the origin of the javascript code you want to execute is always going to be secure, or at least as secure as the javascript receiving it, then it doesn't make much of a difference how it is executed. I guess you can eval the AJAX response, but if it's a simple request, it mine as well just be in script tags in the first place. If it isn't I still think my first comment was the correct way to do this.Ximenez
I could keep track of these things and organize them into functions to be called by the server via the function name concept. But I'm afraid it's too dynamic for this to happen. Don't forget you usually supply arguments to a function, for one thing.Meristic
A
5

Given certain conditions, such as an AJAX request, or (more interestingly) a websocket connection, what is the best way to execute a response from the server?

The main criticism of eval when used to parse message results is that it is overkill -- you are using a sledgehammer to swat a fly with all the extra risk that comes from overpowered tools -- they can bounce back and hit you.

Let's break the kinds of responses into a few different categories:

  1. Static javascript loaded on demand
  2. A dynamic response from a trusted source on a secure channel that includes no content specified by untrusted parties.
  3. A dynamic response from mixed sources (maybe mostly trusted but includes encoded strings specified by untrusted parties) that is mostly data
  4. Side-effects based on data

For (1), there is no difference between XHR+eval and <script src>, but XHR+eval has few advantages.

For (2), little difference. If you can unpack the response using JSON.parse you are likely to run into fewer problems, but eval's extra authority is less likely to be abused with data from a trusted source than otherwise so not a big deal if you've got a good positive reason for eval.

For (3), there is a big difference. eval's extra-abusable authority is likely to bite you even if you're very careful. This is brittle security-wise. Don't do it.

For (4), it's best if you can separate it into a data problem and a code problem. JSONP allows this if you can validate the result before execution. Parse the data using JSON.parse or something else with little abusable authority, so a function you wrote and approved for external use does the side-effects. This minimizes the excess abusable authority. Naive eval is dangerous here.

Arioso answered 13/2, 2012 at 19:19 Comment(11)
With JSONP, however, there's nothing you can do to prevent the source site from dropping malicious JavaScript code directly into the returned script content. JSONP is completely abusable, in other words. (Well, traditional JSONP to third-party domains.)Sastruga
@Pointy, when loading JSONP via <script src=...>, you're definitely right. But if you view JSONP as a message format IdentifierName '(' DataBundle ')', then you could route it through a filtering proxy, load it into an iframe in a separate domain and use cross-frame messaging to get the data, or do a number of other things to mitigate the risk of side-effects from the response. The basic idea that I was getting at is that message formats which separate data from side-effects and allow you to use different strategies to validate each are more securable.Arioso
Since I prefer to use JSON, I've been thinking about getting the server to return a 'packet type' which indicates what should be done with the response; execution or just static use.Meristic
@MikeSamuel yes that's true - that's what I meant by the "traditional" qualification. I've never heard of consuming JSONP like that; it'd be tricky because you'd basically have to parse the whole response with a specialized JSON parser to account for the outer () from the function call. I guess that wouldn't be so bad if you really needed to do it for some reason.Sastruga
@MikeSamuel {"t":0,"p":{"x":5,"y":7}} vs {"t":1,"p":"if(a)b();"} (t=type, p=payload)Meristic
@Pointy, it's not that hard to parse. Pull off the leading function name and open parenthesis, strip the parenthesis off the right side. Parse JSON, lookup function, call function. The tricky part is getting in a position to do that when your JSONP request has to carry credentials. The semi-trusted iframe trick can help there, but comes with x-frame messaging overhead.Arioso
@ChristianSciberras, if you're worried about abusable excess authority, and an attacker can specify the type, then the type doesn't add any protection. If the attacker can control part of the data, but not the type, then it can help you reduce the number of places where an attack can slip through.Arioso
No, the type is there to just separate data packets from executable ones.Meristic
@MikeSamuel oh duhh the close paren (or maybe );) will end the string :-)Sastruga
@MikeSamuel: The difficulty isn't in the parsing, it's in getting access to the text to parse. The whole point of JSON-P is that it gets around the SOP. If you're going to posit that you've already got around it, then you're just writing a JSON parser, which is dead easy (by design). But good luck getting around it.Dollhouse
@T.J.Crowder, please see my other comment about semi-trusted iframes.Arioso
P
5

"Evil" does not mean "forbidden". Sometimes, there are perfectly good reasons to use so-called "evil" features. They are just called "evil" since they can be, and often are, misused.

In your case, the client-side script is only allowed to make requests to "its own" server. This is the same server the original JavaScript came from, so the dynamic response is as trusted as the original code. A perfectly valid scenario for eval().

Plush answered 13/2, 2012 at 19:18 Comment(8)
I've been told that in general, eval() is much slower (in total) than fetching a script into the current scope. I must say all this eval scaremongering is keeping me away from this one since the fetched code should end up in my current scope.Meristic
@ChristianSciberras: eval does indeed play funky games with scope. Specifically, the code is evaluated in the scope in which you call eval, rather than in global scope. So function foo(msg) { eval('alert(msg);'); } foo("Hi"); alerts "Hi" and function foo(msg) { eval('msg = "bye";'); alert(msg); } foo("Hi"); alerts "bye". This is one of the reasons eval is evil. If you're going to execute code from the server, use a script element so at least you're doing it in the usual place. But if that server isn't under your control, understand that there's a lot of trust required.Dollhouse
@T.J.Crowder Is it possible to have a 'buffer' script where I can throw in the javascript for execution?Meristic
@T.J.Crowder -- why is this behavior "funky" or "evil"? It's perfectly reasonable for eval to run the code in the same scope as the function call.Plush
@ChristianSciberras, eval used to be the fastest way to parse JSON, and still is on IE 6. On newer browsers, even non-native JSON parsers can be faster, simply because eval has to have all the machinery to parse a much larger more complex language.Arioso
@FerdinandBeyer: It's "funky" because it's very special. I didn't say it was "evil." eval is only evil in context (e.g., when abused). Like nearly every other technology.Dollhouse
@T.J.Crowder — You literally said "This is one of the reasons eval is evil". I still don't get your point, it is far more intuitive for eval to execute the code in the same scope as eval was called than forcing execution in global scope. Plus, if this is a problem for you, you can easily wrap it in function.Plush
@FerdinandBeyer: You're right, I should have said "that's one of the reasons people say eval is evil". If you wrap eval in a function, the code gets executed in the scope of that function, not the global scope. Most standalone resources get executed in global scope. It matters, particularly for function declarations. If the purpose of the returned code is to provide functions to be used by other code, it's a real problem if they're evaluated in some function wrapper rather than global scope, because then they'll only be available within that wrapper. Unless, of course, that's your goal.Dollhouse
A
5

Given certain conditions, such as an AJAX request, or (more interestingly) a websocket connection, what is the best way to execute a response from the server?

The main criticism of eval when used to parse message results is that it is overkill -- you are using a sledgehammer to swat a fly with all the extra risk that comes from overpowered tools -- they can bounce back and hit you.

Let's break the kinds of responses into a few different categories:

  1. Static javascript loaded on demand
  2. A dynamic response from a trusted source on a secure channel that includes no content specified by untrusted parties.
  3. A dynamic response from mixed sources (maybe mostly trusted but includes encoded strings specified by untrusted parties) that is mostly data
  4. Side-effects based on data

For (1), there is no difference between XHR+eval and <script src>, but XHR+eval has few advantages.

For (2), little difference. If you can unpack the response using JSON.parse you are likely to run into fewer problems, but eval's extra authority is less likely to be abused with data from a trusted source than otherwise so not a big deal if you've got a good positive reason for eval.

For (3), there is a big difference. eval's extra-abusable authority is likely to bite you even if you're very careful. This is brittle security-wise. Don't do it.

For (4), it's best if you can separate it into a data problem and a code problem. JSONP allows this if you can validate the result before execution. Parse the data using JSON.parse or something else with little abusable authority, so a function you wrote and approved for external use does the side-effects. This minimizes the excess abusable authority. Naive eval is dangerous here.

Arioso answered 13/2, 2012 at 19:19 Comment(11)
With JSONP, however, there's nothing you can do to prevent the source site from dropping malicious JavaScript code directly into the returned script content. JSONP is completely abusable, in other words. (Well, traditional JSONP to third-party domains.)Sastruga
@Pointy, when loading JSONP via <script src=...>, you're definitely right. But if you view JSONP as a message format IdentifierName '(' DataBundle ')', then you could route it through a filtering proxy, load it into an iframe in a separate domain and use cross-frame messaging to get the data, or do a number of other things to mitigate the risk of side-effects from the response. The basic idea that I was getting at is that message formats which separate data from side-effects and allow you to use different strategies to validate each are more securable.Arioso
Since I prefer to use JSON, I've been thinking about getting the server to return a 'packet type' which indicates what should be done with the response; execution or just static use.Meristic
@MikeSamuel yes that's true - that's what I meant by the "traditional" qualification. I've never heard of consuming JSONP like that; it'd be tricky because you'd basically have to parse the whole response with a specialized JSON parser to account for the outer () from the function call. I guess that wouldn't be so bad if you really needed to do it for some reason.Sastruga
@MikeSamuel {"t":0,"p":{"x":5,"y":7}} vs {"t":1,"p":"if(a)b();"} (t=type, p=payload)Meristic
@Pointy, it's not that hard to parse. Pull off the leading function name and open parenthesis, strip the parenthesis off the right side. Parse JSON, lookup function, call function. The tricky part is getting in a position to do that when your JSONP request has to carry credentials. The semi-trusted iframe trick can help there, but comes with x-frame messaging overhead.Arioso
@ChristianSciberras, if you're worried about abusable excess authority, and an attacker can specify the type, then the type doesn't add any protection. If the attacker can control part of the data, but not the type, then it can help you reduce the number of places where an attack can slip through.Arioso
No, the type is there to just separate data packets from executable ones.Meristic
@MikeSamuel oh duhh the close paren (or maybe );) will end the string :-)Sastruga
@MikeSamuel: The difficulty isn't in the parsing, it's in getting access to the text to parse. The whole point of JSON-P is that it gets around the SOP. If you're going to posit that you've already got around it, then you're just writing a JSON parser, which is dead easy (by design). But good luck getting around it.Dollhouse
@T.J.Crowder, please see my other comment about semi-trusted iframes.Arioso
S
4

If you're fetching code from a domain you don't control, then handing over the code "raw" to the JavaScript interpreter always means you have to completely trust that domain, or else that you have to not care whether malicious code corrupts your own pages.

If you control the domain, then do whatever you want.

Sastruga answered 13/2, 2012 at 19:18 Comment(5)
No, definitely not doing that. We are assuming our connection is over SSL and there are no eavesdroppers.Meristic
Do you own the domain? Is it under your control? If not, then it doesn't matter whether it's SSL or not. You're placing your trust in some other host, and the integrity of your pages (and possibly your site) rely on that trust not being violated.Sastruga
It's my same domain, with the same security controls used to load the page initially (ie, hopefully SSL).Meristic
@ChristianSciberras well in that case what are you worried about? You're already presumably including JavaScript files directly into your pages. What's the difference in them showing up after page loads?Sastruga
I just want to do this right, somehow better than just using eval(). Some guy once said "eval is never the solution".Meristic
S
2

The server should provide you with data, not code. You should have the server respond with JSON data that your JS code can act accordingly. Having the server send names of functions to be called with myObj[resp](); is still tightly coupling the server logic with client logic.

It's hard to provide more suggestions without some example code.

Sardonic answered 13/2, 2012 at 19:18 Comment(4)
The server logic isn't really important here. Whether you like it or not, there server returns code, presentation and structure. At that point what matters is how you return that stuff.Meristic
I disagree, my AJAX and WebSocket calls never return code. They return data objects that are used by JS code. Putting code in your web services tightly couples your code. An example is that your web services aren't going to be usable by, for example, an iOS application, whereas, if you design your AJAX/WebSockets ensuring only data is retrieved, you should be able to reuse your server side code for the web app, the iOS app, the Android app...Sardonic
And by the way, I don't think eval is always evil. My problem is tightly coupled code between client and server.Sardonic
Good point. I'm trying to keep this as lightly coupled as possible. What the server would return is conditions leading to execution of methods, not anything which is platform specific.Meristic
D
2

Have your server return JSON, and interpret that JSON on the client. The client will figure out what to do with the JSON, just as the server figures out what to do with requests received by the client.

If your server starts returning executable code, you have a problem. NOT because something "bad" is going to happen (although it might), but because your server is not responsible for knowing what the client is or is not suppose to do.

That's like sending code to the server and expected the server to execute it. Unless you've got a REALLY good reason (such as an in-browser IDE), that's a bad idea.

Use eval as much as you want, just make sure you're seperating responsibilites.

Edit:

I see the flaw in this logic. The server is obviously telling the client what to do, simply because it supplied the scripts that the client executes. However, my point is that the server-side code should not be generating scripts on the fly. The server should be orchestrating, not producing.

Dionnedionysia answered 13/2, 2012 at 19:19 Comment(5)
+1 Funnily enough, an in-browser IDE is exactly what I'm doing. :)Meristic
Woohoo: +1! I tend not to get +'s when the "big boys" are responding.Dionnedionysia
Regarding the edit, that's a darn good point I should stick to.Meristic
I don't like my server cooking my food. That's the chef's job.Dionnedionysia
I think Christopher and I are saying the same things.Sardonic

© 2022 - 2024 — McMap. All rights reserved.