Search code examples
javascriptscopesandbox

Set function default this value


Background:

I am running a sandboxed iframe which only has the permission "allow-scripts". In the sandbox a script is loaded with custom js provided by the user. Now i want to manage access to global functions/objects like XMLHttpRequest. Currently i achieve that with the following code:

const CLEARED_WINDOW_SCOPE= {};
const scope = { /* Here goes the globals the script should have access too */};
const propertyNames = Object.getOwnPropertyNames(window);

for(let property of propertyNames){
    CLEARED_WINDOW_SCOPE[property] = undefined;
}

with(CLEARED_WINDOW_SCOPE){
    with(scope){
        (function (window, self, frames, globalThis){
            ${scriptContent}
        }).call(scope, scope, scope, scope, scope); 
    }
}

The script does the following:

  1. Create object with all window property names set to undefined
  2. Create object with all properties the user should have access too
  3. Wrap the user code with two with statements the first clears all globals the second grands access to the defined ones
  4. Wrap the user code with a function that is called with the scope as this value

So far everything works perfectly as excepted.

Problem:

The main problem that i have right now is that when a user defines a function like that:

function aFunction(){
   console.log(this);
}

He gains access to the normal window object because the default this value within a function is the window.

Question:

Is it somehow possible to change the default this value of a function to the this value of the surrounding scope. Since the user creates the script i can't wrap all function calls with aFunction.bind(scope). Or is there any other value to prevent the access to the global window object?


Solution

  • Is it somehow possible to change the default this value of a function to the this value of the surrounding scope.

    No, but you can do the next best(?) thing: setting it to undefined. Just force strict mode:

    "use strict";
    
    // User's code
    (function (){
      console.log(this)
    })();

    Also, I'm not a JavaScript security expert, but JS sandboxing is a really complex topic because of things like prototype pollution.

    Edit: As CherryDT noted in the comments, this method is not completely secure. Users can still access the <iframe> window by creating a function with the Function constructor, for example. Also, a reference to the main window can be obtained through the <iframe> window (window.parent).

    It's maybe OK to use this solution for user-supplied code (since users can just open the DevTools console and start typing), but make sure the code comes from the user and never from a URL search parameter, for example. If the code is completely distrusted, I would recommend to use a well-known library like Google Caja.