我有一个名为action.php的文件,可以执行某些操作。我想将它公开为普通的JSON或JSONP输出。用户将使用如下URL来调用它:
action.php?jsonp=callback
在我的action.php中,我正在做这样的事情
$jsonp = isset $_GET["jsonp"] ? $_GET["jsonp"] : false;
$output = execute_action();
if ($jsonp) {
header('Content-Type: application/javascript');
printf("%s(%s)", $jsonp, json_encode($output));
} else {
header('Content-Type: application/json');
echo json_encode($output);
}
但这对我来说似乎不安全。如果传入jsonp回调参数,我应该验证还是转义它?如果是这样,这样可以防止什么情况,我应该如何在PHP中做到这一点?
我们假设此action.php作为任何网站使用的服务(包括我自己的网站)在互联网上公开。
编辑:为清楚起见,我的问题有两个部分:
您对保护假想的第三方网站免受有害jsonp注射的重要性的意见
现在假设我想使用我的服务来保护第三方网站,我应该验证jsonp参数(即可能只允许某些字符吗?),还是应该转义jsonp输出(如果是的话{{1}我应该使用函数吗?)
对于我将答案标记为已接受,我想对这两个问题提供更多意见。
答案 0 :(得分:3)
由于用户只接收他们自己发送的数据,因此不存在持续注入的真实风险。但是,攻击者仍然可以创建恶意链接并使客户端点击它,以在客户端计算机上执行代码。为了避免攻击者创建这些恶意链接(例如允许从您的域中窃取cookie),您必须转义或验证回调参数。
如果您只是验证它或逃避它,您必须选择。在我看来,逃避并不是真正意义上的。通常我们转义HTML实体以防止攻击并允许HTML字符,如'<' 或'>' 正确显示。但在这种情况下,没有理由诚实用户发送需要转义的字符......因此验证足够且更有意义。
怎么做?
如果您不想考虑注入问题,更安全的方法是为函数设置固定名称并让用户实现它。尽量避免名称冲突。
您还可以验证回调值。它必须是有效的javascript函数名称。您可以使用PHP函数preg_match()
http://de3.php.net/preg_match。
$jsonp = preg_match('/^[$A-Z_][0-9A-Z_$]*$/', $_GET["jsonp"]) ? $_GET["jsonp"] : false;
$output = execute_action();
if ($jsonp) {
header('Content-Type: application/javascript');
printf("%s(%s)", $jsonp, json_encode($output));
} else {
header('Content-Type: application/json');
echo json_encode($output);
}
我不是正则表达式的专家,所以我从Validate a JavaScript function name得到了这个不完整的示例模式。检查正确的正则表达式。
答案 1 :(得分:3)
1)让我们看看......
说某人正常使用您的代码:
action.php?jsonp=callback
这将呈现
callback({"a":1,"b":2,"c":3,"d":4,"e":5})
如果有人尝试更恶意的事情:
action.php?jsonp=alert('foo');//
这将呈现
alert('foo');//({"a":1,"b":2,"c":3,"d":4,"e":5})
但是,这可以通过他们自己网站上的JavaScript链接来实现(例如www.foo.com
):
<script src="http://www.example.com/action.php?jsonp=alert('foo');//"></script>
警报将在www.foo.com
而非www.example.com
的上下文中执行。
当您返回正确的内容类型(Content-Type: application/javascript
)时,这应该会阻止某人将此作为XSS漏洞利用。
2)但出于完整性,我倾向于拒绝jsonp
包含非字母数字的任何请求。编码实际上不是一个选项,因为它需要是一个有效的JavaScript函数。
这种方法对于旧浏览器可能是必要的,因为一些旧版本的IE(以及兼容模式下的较新版本)执行MIME type sniffing,因此如果有人链接到您的页面(使用锚点<a>
在jsonp
参数中有足够的HTML,它们可以诱骗IE将内容呈现为HTML,然后嵌入在这个HTML中的脚本代码将在您的站点的上下文中执行,您将拥有一个XSS漏洞。
在我看来,将参数限制为字母数字字符不会太严格。
要在PHP中执行此操作,请使用ctype_alnum函数,如果为false则拒绝:
if (!ctype_alnum($jsonp)) {
// halt
}