2

Some irrelevant background (read only if you want to say "no, just use an iframe")

TL;DR: This is just an academic discussion. Please don't turn this into an XY question. I am making a static webpage hosted on GitHub pages that connects to some third-party game server implementing a certain WebSocket protocol. I am considering an optional feature to implement some Turing-complete logic to be evaluated client-side without contacting server. I know that <iframe> is going to solve the problem, but I'm looking into other approaches and find out why they don't work.

Scenario

Suppose that I downloaded a string jsCode containing arbitrary JavaScript code from a (potentially malicious) third-party server via a WebSocket. I want to allow the code to implement basic Turing-complete logic, especially form data validation, as well as to touch a restricted set of APIs, e.g. to send some data into the WebSocket that jsCode was received from.

(This is also why iframe was not my first choice; I don't want to create a DOM at all)

The third-party server address is inputted by the user, so in case jsCode does something malicious (to the extent that the user notices), I am expecting to see that all effects are cleared when the user clicks F5.

Another potential way of attacking is a while(true){} that hangs/crashes the page, but since the user knows the server hangs the page, they won't connect to that server next time, so the effects are also cleared when the user clicks F5.

So there are two main things that I don't want the arbitrary code to do:

  • make it look as if the user is in another page (particularly phishing sites), especially downloading files
  • has lasting effect after F5 reloading
    • In particular, my static page stores the connection history (and potentially credentials to other third-party servers) in localStorage, and I don't want the arbitrary script to access it.

Method

The method in question is to redeclare all variables that the script can access. In this way, the script cannot access any JS values (not even global values) except those explicitly passed to it via function parameters:

function evalFunction(jsCode) {
    const vars = ["jsCode", "name", "fx", "ex"] // these may not be in window
    for(const name in window) vars.push(name) // regardless of hasOwnProperty()

    const fx = new Function(`var ${vars.join()}; ${jsCode}`)
    try{
       return [true, fx()]
    }catch(ex){
       return [false, ex]
    }
}

In case I want to expose some stateless libraries such as Math, I can wrap them in closures to pass as arguments to the function. If implemented carefully, this will not expose more danger than the argument-less case like the code above, so let's ignore this part for this question.

I have created a jsfiddle out of the hypothesized mechanism: https://jsfiddle.net/16rb8cxe/6/

It seems to effectively block access to location access.

I am mostly confident that this has other loopholes. Can you provide example code to produce undesired effect as indicated above (or other effects that you think would still be very undesirable)?

(In case anyone notices, I created the same question on CodeReview.SE, but I flagged to close it for off-topic)

  • 1
    [].constructor.constructor("a","alert('XSS')")() or in non-strict: (function(){return this}()).alert('XSS'). I actually attempted the same thing about 5 years ago. You can lock down use strict, but not normal JS. There's a lib i wrote to run sandbox code like eval() does, but with aliases and all that,. like what you have: danml.com/js/subeval.js ... Might be a worth checking out to see if you forgot something i didn't, or vice versa. – dandavis Feb 26 at 17:52
  • Lovely. I didn't remember that my own tool can be used against myself :D – SOFe Feb 26 at 17:54

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.