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:
with
statements the first clears all globals the second grands access to the defined onesscope
as this
valueSo 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?
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.