是否有必要验证或转义jsonp回调字符串

时间:2014-01-06 20:00:16

标签: php json jsonp xss

我有一个名为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作为任何网站使用的服务(包括我自己的网站)在互联网上公开。

编辑:为清楚起见,我的问题有两个部分:

  1. 您对保护假想的第三方网站免受有害jsonp注射的重要性的意见

  2. 现在假设我想使用我的服务来保护第三方网站,我应该验证jsonp参数(即可能只允许某些字符吗?),还是应该转义jsonp输出(如果是的话{{1}我应该使用函数吗?)

  3. 对于我将答案标记为已接受,我想对这两个问题提供更多意见。

2 个答案:

答案 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
}