让我们看一下这个示例有效载荷 (A),编码一次 (B) 和两次 (C):
A. <script> alert(1) </script>
B. %3Cscript%3E alert(1) %3C%2Fscript%3E
C. %253Cscript%253E alert(1) %253C%252Fscript%253E
当应用程序的不同部分对变量是否被编码做出不同的假设时,双重编码可用于绕过 XSS 过滤器。例如考虑以下易受攻击的代码:
$input = htmlentities($_GET["query"]);
echo urldecode($input);
如果只是单编码(如 B 中),此代码将阻止有效负载。默认情况下,PHP URL 会为您解码您的 GET 变量(将 B 转换为 A),因此<
并且>
将被传递给htmlentities
它来中和它们。但是,如果您改为在 C 中发送,它将被解码为 B 的 URL 将htmlentities
不变地通过。由于它在回显之前再次进行了 URL 解码,因此它变成了危险的有效载荷 A。
所以这里的bug就是在XSS过滤器之后还有一层URL解码。当两条线像这样挨着的时候,问题就很明显了。但这两个东西可以在不同的模块中,因此很难检测到。由于很难跟踪哪些字符串是经过 URL 编码的,因此很容易进行额外的解码来确定——毕竟,它通常不会影响未编码的数据。
PHP 手册实际上对此提出了警告:
警告:超全局变量$_GET
和$_REQUEST
已经解码。urldecode()
在元素上使用$_GET
or$_REQUEST
可能会产生意外和危险的结果。
在我看来,手册在这里不够谨慎 - 在过滤 XSS 后解码任何不受信任的数据是危险的,无论它来自哪里。过滤后修改数据时要非常小心!
如需更多阅读,请参阅OWASP。