ThinkPHP的jsonp()默认不安全,因其直接拼接未经校验的input('get.callback')导致XSS或数据泄露;JSONP协议本身依赖服务端校验,而框架仅检查参数存在性、不验证内容合法性,且非法字符会静默失败;应采用白名单校验或手动构造响应,并在可控环境下切换至CORS。

ThinkPHP的jsonp()为什么默认不安全
因为jsonp()直接拼接input('get.callback')进响应体,不做任何校验。攻击者传callback=alert(1)或callback=document.write('<script src="//xss.com"></script>'),服务端照单全收,浏览器执行后就完成XSS或数据外泄。
这不是ThinkPHP写错了,而是JSONP协议本身依赖<script>标签的信任机制——服务端必须自己把关,不能指望浏览器拦截。</script>
-
jsonp()只检查callback参数是否存在,不验证内容合法性 - 非法字符(如
.、[、]、;)会导致内部静默失败,返回空响应,但日志里没报错 - 若项目开了全局CORS中间件,可能覆盖
Content-Type: application/javascript,导致部分浏览器拒绝执行
如何白名单校验callback参数
别信“过滤特殊字符”这种模糊策略,正则漏掉一个Unicode变体就可能绕过。直接用白名单最稳。
推荐两种方式:
立即学习“PHP免费学习笔记(深入)”;
使用 JSON Schema 验证 JSON 数据,从示例 JSON 生成 schema,并将其转换为 TypeScript 接口、Python 数据类或 Markdown 文档。
- 全局配置:在
config/app.php中设置'jsonp_callback' => ['getData', 'onSuccess', 'cb_success'],框架自动比对 - 动态指定:控制器里用
jsonp($data, ['callback' => ['myCb', 'apiCallback']]),按接口粒度控制
如果白名单没命中,ThinkPHP默认返回纯JSON(不带回调),前端JS会报语法错误,但至少不会执行恶意代码。
手动构造JSONP响应更可控
放弃return jsonp($data)这种“一招鲜”,显式控制每一步:
- 先取参:
$cb = input('get.callback') ?: input('get.jsoncallback');(兼容老系统不同参数名) - 再清洗:
$cb = preg_replace('/[^a-zA-Z0-9_]/', '', $cb);,空则abort(400) - 设头:
header('Content-Type: application/javascript; charset=utf-8');,必须加,否则IE/旧版Firefox可能当HTML解析 - 输出:
echo $cb . '(' . json_encode($data, JSON_UNESCAPED_UNICODE) . ');'; exit;,exit强制终止,避免后续代码污染响应
什么时候该彻底停用JSONP
只要你的前后端都可控,且不需要支持IE8及更老浏览器,就该立刻切换到CORS。
JSONP的硬伤没法补:
- 只能GET,没法传
Authorization头,所有需登录态的接口天然不兼容 - 服务端完全看不到真实
Origin,无法做来源白名单,CSRF风险敞口大 - 错误全靠前端
try/catch捕获,服务端日志里连请求都没留下,排查劫持事件基本靠猜
真正难处理的不是技术切换,而是那些还在用jQuery 1.x的老前端——它们调用JSONP时参数名可能是jsoncallback,而新接口只认callback。这种兼容性缝合,比安全加固更容易出问题。


















