我是不是需要清理 JSONP 调用中的回调参数?

Posted

技术标签:

【中文标题】我是不是需要清理 JSONP 调用中的回调参数?【英文标题】:Do I need to sanitize the callback parameter from a JSONP call?我是否需要清理 JSONP 调用中的回调参数? 【发布时间】:2011-02-16 03:33:35 【问题描述】:

我想通过 JSONP 提供网络服务,并且想知道是否需要清理回调参数中的值。

我当前的服务器端脚本目前看起来像这样(或多或少。代码在 php 中,但实际上可以是任何东西。):

header("Content-type: application/json; charset=utf-8");
echo $_GET['callback'] . '(' . json_encode($data) . ')';

这是一个经典的 XSS 漏洞。

如果我需要对其进行消毒,那该怎么做?我无法找到有关可能允许的回调字符串的足够信息。我引用Wikipedia:

虽然填充(前缀)通常是在浏览器的执行上下文中定义的回调函数的名称,但它也可以是变量赋值、if 语句或任何其他 javascript 语句前缀。

【问题讨论】:

【参考方案1】:

您要确保回调是有效的标识符,可以是字母数字、下划线或 $。它也不能是保留字(为了彻底,我会确保它不是undefinedNaNInfinity)。这是我使用的测试:

function valid_js_identifier( $callback )
    return !preg_match( '/[^0-9a-zA-Z\$_]|^(abstract|boolean|break|byte|case|catch|char|class|const|continue|debugger|default|delete|do|double|else|enum|export|extends|false|final|finally|float|for|function|goto|if|implements|import|in|instanceof|int|interface|long|native|new|null|package|private|protected|public|return|short|static|super|switch|synchronized|this|throw|throws|transient|true|try|typeof|var|volatile|void|while|with|NaN|Infinity|undefined)$/', $callback);

许多保留字毫无意义,但其中一些可能会导致错误或无限循环。

重要提示:不要只是通过替换字符来清理输入;修改后的回调可以正常运行,并且返回的数据将无法正确处理(甚至可能被错误的函数处理)。您想测试输入是否有效,如果不是则抛出错误。这将避免意外行为并通知开发人员需要不同的回调。

注意:这是一个更安全但有限的 JSONP 版本,它不允许表达式或细化。我发现它适用于大多数应用程序,特别是如果您使用 jQuery 和 $.getJSON

【讨论】:

【参考方案2】:

是的,当callback就像

(function xss(x)evil())

当你从 php 回显时,会看起来像

(function xss(x)evil())(json)

函数 xss 将运行,并且 evil() 可以是一些将 cookie 发送到其他地方的代码。

因此,将其清理为仅有效的函数名称,例如,将其限制为字母数字

【讨论】:

您可以使用 ctype_alpha($_GET['callback']) 轻松检查这种情况下您是否使用英语语言环境,否则过滤器功能可以很好地工作。另请注意,标头应设置为 'content-type: application/json;返回 jsonp 响应时的 charset=utf-8'。 你应该使用 application/javascript 而不是 application/json!! 这个答案假定callback 的值是受攻击者控制的。如果目标页面上存在一些 XSS 漏洞(本身就是一个问题),或者它是攻击者的页面发出 JSONP 请求(在这种情况下脚本注入无关紧要),这是正确的。正确的?所以目前还不清楚为什么需要消毒。【参考方案3】:

是的。

正如@YOU 所述,攻击者可以制作一个回调参数,评估为恶意javascript,或更糟的是malicious Flash。

验证回调不是保留字并且是@Brett-Wejrowski 所述的字母数字是一个好的开始。

Google、Facebook 和 Github 正在缓解 Rosetta Flash 漏洞,方法是在 jsonp 回调之前添加一个空注释,例如 /**/。

另一种方法是像 ExpressJS 一样返回更安全的 javascript 表达式:

typeof callbackstring === 'function' && callbackstring(.....);

【讨论】:

【参考方案4】:

是的,您需要清理回调参数。

JSONP 基本上是一种自我造成的 XSS 攻击。当(临时)将带有 url 的脚本标签插入到不同的主机名并让它调用页面上的全局函数或方法时,重要的是至少要采取一些预防措施,将“回调”限制为仅是回调名字。

从语法上讲,回调名称应该像标识符一样简单。您可以考虑对象属性。我建议不要使用括号,因为这可能会启用函数调用等。

以下是同时支持 JSON 和 JSONP 的基本 API 示例。它是用 PHP(从 MediaWiki 的 API 简化)编写的,但可以用其他编程语言创建类似的结构。

<?php

// Simulate the response data
$responseData = [
    'foo' => 'bar',
    'count' => ['one', 'two', 'three'],
    'total' => 3,
];

// Prepare to send the response
$prefix = '';
$suffix = '';
$ctype = 'application/json';

if (isset($_GET['callback'])) 
    $ctype = 'text/javascript';
    // Sanitize callback
    $callback = preg_replace("/[^][.\\'\\\"_A-Za-z0-9]/", '', $_GET['callback']);

    $prefix = $callback . '(';
    $suffix = ')';


// Send the response
header("Content-Type: $ctype; charset=UTF-8", true);
print $prefix . json_encode($responseData) . $suffix;
exit;

【讨论】:

以上是关于我是不是需要清理 JSONP 调用中的回调参数?的主要内容,如果未能解决你的问题,请参考以下文章

jsonp回调函数没有被调用

Sencha Touch 2 - 我如何通过回调调用 JsonP 中的细节

关于JSONP

跨域访问JSONP CORS

手写一个jsonp实现

在 JavaScript 中调用/执行后是不是需要删除回调函数?