一文学会 Jsonp (JSON_with_Padding) 跨域请求

文章目录

  • 流程
  • 缺点
  • 名称由来
  • demo
  • JSONP安全性问题
      • CSRF攻击
      • 5XSS漏洞
      • 服务器被黑,返回一串恶意执行的代码
  • 封装工具函数
  • 真实案例:获取淘宝搜索关键字推荐

流程

script 标签 src 属性发起的请求不受同源策略的限制,并且 script 标签默认类型是text/javascript。只要定义了这个类型,则script请求的内容就会被浏览器以JS代码来执行。这就为跨域提供了可能性。

既然可以执行JS代码,那我们就可以用函数包裹真实数据。

定义一个带有形参的函数,将函数名通过url的额外参数传递给服务器,服务器拿到函数后,将函数名拼成函数调用的字符串形式,并将响应数据序列化成字符串,以实参的形式传递给函数,然后返回给客户端。

src 属性请求回来后,因为 script 标签类型是 text/javascript,所以会执行请求回来的代码,也就相当于在执行这个函数。而这个函数的实参就是请求的数据,至此实现了跨域请求数据。

流程:

  1. 在发请求先,准备一个前后端约定好的全局接收函数,如 fn
  2. 在 html 创建 script 标签,src 带着 函数名"fn" 发出请求
  3. 服务器传入数据,响应 fn({"name": "ikun"})字符串给客户端
  4. 因为是 script 标签,客户端会执行fn({name: "ikun"}),也就是调用了定义的 fn 函数。
  • 数据会自动反序列化进内存

缺点

因为是使用 url 额外参数的形式传递的函数名,url 都是 get 请求,所以 jsonp 也只能支持 get 请求。

名称由来

平常的前后端数据交互一般是 json 格式,现在服务端响应时函数包裹了 json 数据,所以是 json with padding(包裹),也就是 jsonp。

demo

<body>
  
  <button>Click me</button>
  
  <script>
    
    const createJsonpRequest = (url, callback) => {
      return new Promise((resolve, reject) => {
        const script = document.createElement("script");
        script.src = `${url}?callback=${callback}`;
        script.type = "text/javascript";
        script.async = true;
        
        document.body.appendChild(script);
        
        script.onload = () => {
          document.body.removeChild(script);
          resolve();
        };
        script.onerror = () => {
          document.body.removeChild(script);
          reject();
        };
      });
    };

    document.querySelector("button").addEventListener("click", async () => {
      const url = "http://localhost:3000/jsonp";

      await createJsonpRequest(url, "callback");

      console.log("请求完毕");
    });

    window.callback = res => {
      // do something with the response
      console.log(res);
    };
  </script>
</body>
import "reflect-metadata";
import express from "express";
import { Container } from "inversify";
import { InversifyExpressServer, controller, httpGet, queryParam, response } from "inversify-express-utils";

@controller("/jsonp")
export class MyController {
  
    @httpGet("/")
    public async getJsonp(@queryParam("callback") callback: string, @response() res: express.Response) {
        const data = { name: "John", age: 30 };
        const jsonp = `${callback}(${JSON.stringify(data)})`;
        res.type("application/javascript").send(jsonp);
    }
}

const container = new Container();
container.bind(MyController).to(MyController);
const server = new InversifyExpressServer(container);

server.setConfig(app => {
    app.use(express.json());
});

const app = server.build();

app.listen(3000, () => console.log("server is running at http://localhost:3000"));

JSONP安全性问题

CSRF攻击

前端构造一个恶意页面,请求JSONP接口,收集服务端的敏感信息。如果JSONP接口还涉及一些敏感操作或信息(比如登录、删除等操作),那就更不安全了。

解决方法:验证JSONP的调用来源(Referer),服务端判断 Referer 是否是白名单,或者部署随机 Token 来防御。

5XSS漏洞

不严谨的 content-type 导致的 XSS 漏洞,想象一下 JSONP 就是你请求 http://abc.com?callback=douniwan, 然后返回 douniwan({ data }),那假如请求 http://abc.com?callback=({ data })了吗,如果没有严格定义好 Content-Type( Content-Type: application/json ),再加上没有过滤 callback 参数,直接当 HTML 解析了,就是一个赤裸裸的 XSS 了。

解决方法:严格定义 Content-Type: application/json,然后严格过滤 callback 后的参数并且限制长度(进行字符转义,例如<换成&lt,>换成&gt)等,这样返回的脚本内容会变成文本格式,脚本将不会执行。

服务器被黑,返回一串恶意执行的代码

可以将执行的代码转发到服务端进行校验 JSONP 内容校验,再返回校验结果。

封装工具函数

(function (global) {
    var id = 0,
        container = document.getElementsByTagName("head")[0];

    function jsonp(options) {
        if(!options || !options.url) return;

        var scriptNode = document.createElement("script"),
            data = options.data || {},
            url = options.url,
            callback = options.callback,
            fnName = "jsonp" + id++;

        // 添加回调函数
        data["callback"] = fnName;

        // 拼接url
        var params = [];
        for (var key in data) {
            params.push(encodeURIComponent(key) + "=" + encodeURIComponent(data[key]));
        }
        url = url.indexOf("?") > 0 ? (url + "&") : (url + "?");
        url += params.join("&");
        scriptNode.src = url;

        // 传递的是一个匿名的回调函数,要执行的话,暴露为一个全局方法
        global[fnName] = function (ret) {
            callback && callback(ret);
            container.removeChild(scriptNode);
            delete global[fnName];
        }

        // 出错处理
        scriptNode.onerror = function () {
            callback && callback({error:"error"});
            container.removeChild(scriptNode);
            global[fnName] && delete global[fnName];
        }

        scriptNode.type = "text/javascript";
        container.appendChild(scriptNode)
    }

    global.jsonp = jsonp;

})(this);

使用示例:

jsonp({
    url : "www.example.com",
    data : {id : 1},
    callback : function (res) {
        console.log(res);
    }
});

真实案例:获取淘宝搜索关键字推荐

看一下淘宝的搜索框,关键字联想推荐接口的响应数据格式,就知道这是 jsonp 的接口。

json 数据被函数包裹,__jp2 为函数名。

__jp2({
    "result": [
        ["ikun车贴", "4541.673992673993"], 
        ["i酷neo9手机", "4705.093023255814"], 
        ["ikun手办", "4486.0609756097565"]
    ]
})

jsonp 是跨域的,所以我们也能获取到这些数据。

<body>
  <form>
    <input type="text" placeholder="请输入关键字" />
    <button type="submit">搜索</button>
  </form>
  <ul></ul>

  <script>
    const renderList = data => {
      const ul = document.querySelector("ul");
      ul.innerHTML = "";
      data.result.map(item => {
        const li = document.createElement("li");
        li.textContent = item[0];
        ul.appendChild(li);
      });
    };

    document.querySelector("form").addEventListener("submit", e => {
      e.preventDefault();

      const keywordUrl = encodeURIComponent(e.target.elements[0].value);

      jsonp({
        url: `https://suggest.taobao.com/sug?k=1&area=c2c&q=${keywordUrl}&code=utf-8&ts=1713023304435&callback=callback`,
        callback: function (res) {
          renderList(res);
        }
      });
    });
  </script>

image.pngimage.png

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/545062.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Leetcode刷题(位运算)

一、 476. 数字的补数 根据题意写即可 代码 class Solution:def findComplement(self, num: int) -> int:l list(bin(num)[2:])for i in range(len(l)):if l[i]0:l[i]1else:l[i]0return int("0b""".join(l),2)位运算 class Solution:def findComple…

Android Surface的跨进程绘制,如何绘制xml布局给Surface,全网独一份

工作中遇到了这样一个需求 需求&#xff1a;需要将一个自定义View或者自定义布局通过跨进程方式传递给第二个应用来展示&#xff0c;第一个应用负责布局的渲染&#xff0c;第二个应用不需要关心第一个应用的业务和实现&#xff0c;仅提供SurfaceView占位及展示 方案&#xff…

AliyunCTF 2024 - BadApple

文章目录 前言环境搭建漏洞分析漏洞利用参考 前言 本文首发于看雪论坛 https://bbs.kanxue.com/thread-281291.htm 依稀记得那晚被阿里CTF支配的恐惧&#xff0c;今年的阿里CTF笔者就做了一道签到PWN题&#xff0c;当时也是下定决心要学习 jsc pwn 然后复现这道 BadApple 题目…

30元腾讯云服务器搭建幻兽帕鲁Palworld多人联机游戏,畅玩

幻兽帕鲁太火了&#xff0c;官方palworld服务器不稳定&#xff1f;不如自建服务器&#xff0c;基于腾讯云幻兽帕鲁服务器成本32元全自动部署幻兽帕鲁服务器&#xff0c;超简单有手就行&#xff0c;全程自动化一键部署10秒钟即可搞定&#xff0c;无需玩家手动部署幻兽帕鲁游戏程…

Python基础整理(一万三千字)(一)

目录 一、Python解释器 解释器的作用&#xff1a; 下载Python解释器&#xff1a; 安装Python解释器&#xff1a; 二、注释 三、变量 定义变量&#xff1a; 标识符&#xff1a; 命名习惯&#xff1a; 变量使用&#xff1a; 变量的数据类型&#xff1a; 四、输出 格式化输出 …

聚酰亚胺PI材料难于粘接,用什么胶水粘接?那么让我们先一步步的从认识它开始(二十六): 聚酰亚胺PI材料为什么难于粘接

聚酰亚胺PI材料为什么难于粘接 聚酰亚胺&#xff08;PI&#xff09;材料难以粘接主要是由于其特殊的化学结构和物理性质&#xff1a; 化学稳定性&#xff1a;聚酰亚胺PI材料具有出色的化学稳定性&#xff0c;这使其对大多数化学溶剂和酸碱溶液都表现出良好的抵抗性&#xff0c;…

23电赛D题 CORDIC算法实践——Chisel计算对数函数

一、介绍 在本专栏之前的文章中:用Chisel快速搭建FFT流水线电路Chisel实践 —— 短时傅里叶变换模块的实现与测试 已经介绍到了如何使用Chisel开发FFT运算模块和STFT模块&#xff0c;此篇文章将详细介绍如何使用Chisel进行对数运算模块的开发。 如何使用硬件语言实现对数运算&…

得帆用户有福了!全新社区论坛携手AI助手华丽上线,积分好礼等你拿!

盼望着&#xff0c;盼望着&#xff0c;春天的脚步近了&#xff0c;得帆云社区迎来全新升级&#xff0c;社区论坛携手AI知识库助手上线了&#xff01; 得帆云官方社区论坛&#xff1a; https://edu.definesys.cn/community/community-forum 您也可以点击本文末尾左下方“阅读…

.rdl.data是什麼文件

https://learn.microsoft.com/zh-cn/sql/reporting-services/tools/reporting-services-in-sql-server-data-tools-ssdt?viewsql-server-ver16&redirectedfromMSDN

如何在Odoo 17库存中通过批次号和序列号追踪产品

在Odoo 17库存管理中&#xff0c;通过批次号和序列号追踪产品是一种确保产品从生产到销售全程可追溯的重要方式。在产品打包时或生产过程中会分配这些编号。批次号是指应用于具有相似属性的一组产品的一系列数字或代码&#xff0c;而序列号则是分配给特定单一物品的独特编号。O…

MATLAB5:数据和函数的可视化

文章目录 一、实验目的二、实验内容三、仿真结果四、实践中遇到的问题及解决方法 一、实验目的 1. 掌握基本的二维绘图中曲线图的绘制方法。   2. 掌握三维绘图中曲面图的绘制方法。   3. 掌握三维绘图中网线图的绘制方法。   4. 了解三维表面图的绘制方法。   5. 了解…

【Java框架】Mybatis教程(一)——环境搭建及基本CRUD操作

目录 持久化与ORMORM&#xff08;Object Relational Mapping&#xff09;ORM解决方案包含下面四个部分 MyBatis简介特点MyBatis框架优缺点优点缺点 搭建MyBatis开发环境步骤1. 创建Maven工程&#xff0c;导入MyBatis依赖的组件2. 编写MyBatis核心配置文件(mybatis-config.xml)示…

【C 数据结构】静态链表

文章目录 【 1. 基本原理 】1.1 静态链表中的节点1.2 备用链表 【 2. 静态链表的创建 】2.1 实例1 - 创建静态链表&#xff0c;指定值2.2 实例2 - 创建静态链表&#xff0c;默认值 【 3. 静态链表 添加元素 】【 4. 静态链表 删除元素 】【 5. 静态链表 查找元素 】【 6. 静态链…

腾讯EdgeOne产品测评体验—基于EO新特性与传统CDN的对比以凸显EO绝对优势【以导航站为例】

精益求精&#xff0c;卓越非凡。 ——《论语集注》 EdgeOne 作为腾讯云下一代的 CDN &#xff0c;提供域名解析、动静态智能加速、TCP/UDP 四层加速、DDoS/CC/Web/Bot 防护、边缘函数计算等一体化服务&#xff0c;也支持用户按业务需求&#xff0c;配置自定义复杂访问控制规…

Qt配置外部库(Windows平台)

这里以C的外部库nlopt为例子来示范&#xff0c;右键工程选择添加库&#xff0c;然后选择库文件的目录&#xff08;dll.a&#xff09;&#xff0c;会自动设置好包含路径&#xff08;一般是include的目录&#xff09;&#xff0c;添加库&#xff08;最下面一行&#xff09; &…

【Java】maven传递依赖冲突解决

传递依赖的概念&#xff1a; 传递依赖:&#xff1a; A.jar 依赖 B.jar, B.jar 依赖 C.jar, 这个时候我们就说B是A的直接依赖, C是A传递依赖; 传递依赖可能会产生冲突: 联系着上面, 新导入一个jar包D.jar, D依赖C.jar, 但是B依赖的1.1版本, 而D依赖的是1.2版本, 这时候C这个j…

ROS2从入门到精通1-3:详解ROS2动作通信机制与自定义动作

目录 0 专栏介绍1 动作通信模型2 动作模型实现(C)3 动作模型实现(Python)4 自定义动作 0 专栏介绍 本专栏旨在通过对ROS2的系统学习&#xff0c;掌握ROS2底层基本分布式原理&#xff0c;并具有机器人建模和应用ROS2进行实际项目的开发和调试的工程能力。 &#x1f680;详情&a…

设计模式——观察者模式17

观察者模式指多个对象间存在一对多的依赖关系&#xff0c;当一个对象的状态发生改变时&#xff0c;所有依赖于它的对象都得到通知并被自动更新。这种模式有时又称作发布-订阅模式。 中介者模式是N对N的双向关系。观察者模式是1对N的单向关系。 设计模式&#xff0c;一定要敲代码…

【Linux网络编程】UDP协议

UDP协议 1.再谈端口号端口号划分认识知名端口号(Well-Know Port Number)两个问题netstatpidof 2.UDP协议2.1UDP的特点2.2面向数据报2.3UDP的缓冲区2.4UDP使用注意事项2.5基于UDP的应用层协议 喜欢的点赞&#xff0c;收藏&#xff0c;关注一下把&#xff01; 1.再谈端口号 端口…

如何选择适用于Mac的文件恢复软件?适用于 Mac 的最佳数据恢复软件清单

有人会说&#xff0c;我们的数字生活正变得几乎和我们的物理生活一样重要。我们在线工作&#xff0c;将记忆保存在数码照片库中&#xff0c;在信使中交流&#xff0c;并保留各种文档的数字扫描。 每个人都知道备份是必不可少的。建议每天至少同步一个数字备份&#xff08;例如…