我正试图解决一个portswigger实验室(https://portswigger.net/web-security/cross-site-scripting/contexts/lab-javascript-url-some-characters-blocked)我不明白为什么当表达式窗口+''被省略时,这个代码没有弹出警报:
x=x=>{onerror=alert; throw 1337},toString=x,window+''
如果能对这个代码片段进行完整的解释,我们将不胜感激,谢谢。
首先,代码使用逗号运算符(另请参阅逗号在JavaScript表达式中的作用?(。逗号运算符的作用是允许您计算多个表达式。特别是在这种情况下,你有三个:
x=x=>{onerror=alert; throw 1337},toString=x,window+''
// ^______________________________^ ^________^ ^_______^
// 1 2 3
让我们逐一检查一下:
关于隐式全局变量的简要说明
这将与以下大部分部分相关,所以我想先澄清一下。任何时候分配给任何未用var
、let
或const
声明的名称时,它都会隐式分配给全局对象上的属性。在浏览器中,全局对象是window
,因此任何隐含的全局都会转到那里:
console.log("before assignment");
console.log("'foo' in window:", 'foo' in window);
console.log("window.foo:", window.foo);
foo = 42;
console.log("after assignment");
console.log("'foo' in window:", 'foo' in window);
console.log("window.foo:", window.foo);
箭头函数
x=x=>{onerror=alert; throw 1337}
定义
这将创建一个新的箭头函数,并将其分配给x
。由于没有变量声明,x
将是一个隐式全局,因此附加到window
。这并不是很有用——这样做可能是为了保存几个字符。该函数还接受一个名为x
的参数,但对它不起任何作用。同样,没有什么用处-只保存一个字符,否则需要将其定义为()=>
。
车身部分1
更有趣的是这个函数的作用。
onerror=alert
首先,它将onerror
分配给alert
。这两者都将是全局错误处理程序和作为引用传递的全局alert()
函数。因此,每当任何错误发生时,它都会显示一个警报:
window.onerror = alert;
JSON.parse("{"); // parsing error
请参阅函数调用和函数引用之间的区别是什么?以及在JavaScript中调用带括号和不带括号的函数的区别,以了解有关如何使用函数引用的更多信息。简而言之,如果您将一个函数分配给一个处理程序,那么调用该处理程序实际上会运行该函数。
车身部分2
然后,箭头函数抛出一个带有throw 1337
的错误。在JavaScript中,您可以分配任何要抛出的值,所以它是否是错误并不重要。因此,抛出一个数字是有效的。抛出的值实际上并不相关,因为重要的是抛出一个错误。由于函数的第一部分,此throw
语句将触发全局错误处理程序。这是一个演示(格式化(:
x = x => {
onerror = alert;
throw 1337
}
x();
到目前为止,一切都很好,但这不是代码中调用函数的方式。让我们进入下一部分。
覆盖toString
toString=x
此部分将覆盖window
上的toString
方法,并将其更改为x
函数。因此,无论何时显式或隐式将window
转换为字符串,它都将执行x
。
x = x => {
onerror = alert;
throw 1337
}
toString = x;
window.toString();
将window
转换为字符串
在JavaScript中,当您尝试将任何值与字符串连接时,该值也将隐式转换为字符串。这通常通过调用toString()
方法来完成:
const value1 = { "foo": 42 };
const value2 = {
"foo": 42,
toString() {
return `foo is ${this.foo}`;
}
};
const value3 = {
"foo": 42,
toString() {
return "Hello world";
}
};
console.log(value1 + ""); //default `toString()` method of objects
console.log(value2 + "");
console.log(value3 + "");
同样的事情发生在代码的最后部分:
window+''
这将:
- 触发转换为字符串
- 在
window
上调用toString
方法 - 使用
toString=x
用x
覆盖 - 调用
x
函数 - 将全局错误处理程序设置为仅为
alert
(onerror=alert) and immediately throws an error (
抛出1337`( - 调用全局错误处理程序
为什么省略window+''
不会引发错误
希望从上面可以清楚地看到,但要直接解决这个问题,需要代码来引发之前定义的整个反应链
以下是完整的代码,为了清晰起见,提供了解释和格式:
//create a function
window.x = x => {
onerror = alert; //changes the global error handler
throw 1337 //throws an error to trigger the error handler
};
//overwrite the `toString` method of `window` so it always throws an error
window.toString = window.x;
//implicitly call `window.toString()` by performing a string concatenation
window + '';