Proper way to execute JavaScript returned via AJAX

2019-05-06 20:37发布

问题:

Say I get a response to a request for an AJAX load of data with a mix of JavaScript and HTML, e.g.:

<script>window.alert('Hello World!');</script>
<p>This is a paragraph. Lorem ipsum dolor sit amet...</p>

If I just place that response into a div or other container, the script doesn't get executed automatically. I know this can be done via the eval() function (as noted in the example below), but eval is evil, so how can I do this properly? Note: I am not using jQuery.

The following is an example of the AJAX loader:

function Load(id,url){
    var ajax=new XMLHttpRequest();
    ajax.onreadystatechange=function(){
        if(ajax.readyState!=4)return;
        var obj=document.getElementById(id);
        if(!obj)return;
        obj.innerHTML=ajax.responseText;

        // load any scripts
        var s=obj.getElementsByTagName('script');
        for(var i=0;i<s.length;++i)window.eval(s[i].innerHTML); // <-- bad
    }
    ajax.open("GET",url,true);
    ajax.send(null);
}

回答1:

Please note that you're taking input from the user and running it in the context of a script on your site. So the script can do anything that JavaScript running on your browser/domain would have the ability to do (including cookie stealing, XSS, drive-by malware, etc.).

The only thing you can realistically do to mitigate the risks is to not eval() user-provided content. I'd suggest to consider the following alternatives:

  1. Use iframe as an environment to run user's script: http://dean.edwards.name/weblog/2006/11/sandbox/
  2. Use Caja. It allows websites to safely embed DHTML web applications from third parties, and enables rich interaction between the embedding page and the embedded applications. It uses an object-capability security model to allow for a wide range of flexible security policies. http://code.google.com/p/google-caja/


回答2:

eval isn't particularly evil in this scenario, it's not a lot different than, say dynamically adding a script tag which pulls down a .js file and runs it. That said, there are other options, for instance you can dynamically create a script tag, create a text node with the contents of the script tag you pulled down, and add that to the document. Unlike the innerHTML technique, that will actually run the contents. The only advantage over eval, really, is that you might get a more meaningful stack trace etc if it crashes or has a syntax error.

var newScriptTag = document.createElement('script');
newScriptTag.appendChild(document.createTextNode(
     origScriptTag.innerHTML)
document.body.appendChild(newScriptTag);


回答3:

I solved this today by putting my JavaScript at the bottom of the response HTML.

I had an AJAX request that returned a bunch of HTML that was displayed in an overlay. I needed to attach a click event to a button in the returned response HTML/overlay. On a normal page, I would wrap my JavaScript in a "window.onload" or "$(document).ready" so that it would attach the event handler to the DOM object after the DOM for the new overlay had been rendered, but because this was an AJAX response and not a new page load, that event never happened, the browser never executed my JavaScript, my event handler never got attached to the DOM element, and my new piece of functionality didn't work. Again, I solved my "executing JavaScript in an AJAX response problem" by not using "$(document).ready" in the head of the document, but by placing my JavaScript at the end of the document and having it run after the HTML/DOM had been rendered.