EMwI插件更新:防XSS攻击
Posted 子子翔的技艺空间
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了EMwI插件更新:防XSS攻击相关的知识,希望对你有一定的参考价值。
8月底的时候External Media without Import插件在github上收到一个pull request。对方指出我的代码存在XSS漏洞。惭愧,直到最近才腾出时间仔细研究他说的问题。插件的1.0.2版本合并了对方的pull request,修复了该漏洞。
在修复之前,我的插件中有如下代码:
function admin_post_add_external_media_without_import() {
$info = add_external_media_without_import();
$redirect_url = 'upload.php';
if ( ! isset( $info['id'] ) ) {
$redirect_url = $redirect_url . '?page=add-external-media-without-import&url=' . urlencode( $info['url'] );
$redirect_url = $redirect_url . '&error=' . urlencode( $info['error'] );
$redirect_url = $redirect_url . '&width=' . urlencode( $info['width'] );
$redirect_url = $redirect_url . '&height=' . urlencode( $info['height'] );
$redirect_url = $redirect_url . '&mime-type=' . urlencode( $info['mime-type'] );
}
wp_redirect( admin_url( $redirect_url ) );
exit;
}
这个函数将width 、height 和mime-type等信息通过urlencode编码后设置为url的参数,然后重定向到该url。在url的响应函数中,我调用了urldecode读取来这些信息:
<label><?php echo __('Width'); ?></label>
<input id="emwi-width" name="width" type="number" value="<?php echo urldecode( $_GET['width'] ); ?>">
<label><?php echo __('Height'); ?></label>
<input id="emwi-height" name="height" type="number" value="<?php echo urldecode( $_GET['height'] ); ?>">
<label><?php echo __('MIME Type'); ?></label>
<input id="emwi-mime-type" name="mime-type" type="text" value="<?php echo urldecode( $_GET['mime-type'] ); ?>">
问题就出在这段响应函数的代码中。从 $_GET中取出的值已经是经过 urldecode的。对$_GET再调用urlencode就可能会出问题。比如 $info['mime-type'] = 'image/svg+xml'的情况下,urlencode( $info['mime-type'] )的结果是 'image%2fsvn%2bxml', $_GET['mime-type']的值已经是解码后的 'image/svn+xml',再对它调用一次urldecode就变成了'image/svn xml',这就导致了错误的mime-type。PHP官方文档对这个问题也有说明。
然而直接使用 $_GET也有安全性问题。以响应函数中将mime-type的值显示在输入框的代码为例,假设我们直接将 $_GET的值打印到 input的value中:
<input id="emwi-mime-type" name="mime-type" type="text" value="<?php echo $_GET['mime-type']; ?>">
这种情况下如果 $_GET['mime-type'] 的值当中包含一段可执行代码,那其中的代码就会在浏览器中执行。比如,如果url的末尾是mime-type=”><script>alert(window.location.hash)%3B<%2Fscript>”#Attack!,那么上面的php代码在浏览器中的输出就会变成下面这样:
<input id="emwi-mime-type" name="mime-type" type="text" value="\"><script>alert(window.location.hash);</script>"\"">
(上面这行代码是在Chrome中实验得出的结果,其中的\可能是浏览器自己转义加上的。)
于是用户浏览网页的时候, alert调用就会被执行,弹出Attack!提示框。
这样会有什么问题呢?假如有一个恶意用户老王。他构造了一个url,url的mime-type(或其它任意参数)实际包含了一段老王自己编写的代码。然后他把这个url发给小红让小红点击。不明就里的小红点击了这个url之后,由于网站的PHP代码不够健壮,将 $_GET 直接输出到了网页中,因此老王的代码就得以在小红的浏览器中执行。这时老王的代码就可以做很多事,比如窃取小红浏览器里的cookie,以及存储在localStorage里的用户名、密码等隐私信息。
老王的这种手段就叫XSS攻击,全称Cross-Site Scripting,跨站脚本攻击。其原理和SQL注入类似,均是利用网站后台代码的漏洞,在参数里填入可执行代码,从而将攻击者的代码注入到本不允许其执行的地方(在XSS场景中就是用户浏览器,在SQL注入场景中则是后台数据库),从而实现攻击者的目的。
为了应对XSS攻击,就需要对$_GET的值做些处理,对其中的特殊字符进行转义,比如将<转换成<,然后才能将其输出到网页的html中。WordPress为此提供了一个很方便的函数: esc_html (见:https://codex.wordpress.org/Function_Reference/esc_html)。这个函数不但会对html特殊字符进行转义,还会检查非法的UTF-8字符,考虑了各种情况。
因此将 $_GET 的值输出到网页之前应先对其调用 esc_html ,于是上文将 $_GET 的值打印到 input 的 value 中的代码应改为:
<input id="emwi-mime-type" name="mime-type" type="text" value="<?php echo esc_html( $_GET['mime-type'] ); ?>">
这样整个$_GET['mime-type']的值都会被当成纯字符串而不存在被执行的可能。
值得一提的是,现代主流浏览器大都已经对XSS攻击采取了防护措施。比如在Chrome上浏览含有XSS漏洞的网页的话,其实会被Chrome拦截(Safari也一样):
如果要重现上文写到的XSS漏洞,允许攻击者代码执行的话,需要从命令行启动Chrome,并且用 --disable-xss-auditor 选项关闭Chrome的XSS检查。Mac上的启动命令如下:
'/Applications/Google Chrome.app/Contents/MacOS/Google Chrome' --disable-xss-auditor
以上是关于EMwI插件更新:防XSS攻击的主要内容,如果未能解决你的问题,请参考以下文章