跳到主要内容

XHR 返回内容劫持

本章我们将学习到如何对 xhr 返回的内容进行劫持。

特别鸣谢

本章代码为 cxxjackie 大佬所作,论坛地址

本章仅进行代码的分析和学习

逻辑分析

我们先输出一下 xhr ,可以看到 xhrresponseresponseText 等在原型链 prototype 上:

这两个属性都是只有 get 没有 set,这代表我们只可以获取他而不可以设置他的内容。

所以我们书写代码如下

// 首先我们对xhr的open函数进行劫持,需要获取原open函数
// open是初始化xhr的内容,send是发送数据
// 这里我们在初始化内容的时候进行劫持。
// 我们其实可以在任何一个我们满足劫持需求的地方进行劫持
const xhrOpen = XMLHttpRequest.prototype.open;
XMLHttpRequest.prototype.open = function () {
// 这里保存了this到xhr变量,为了提高代码的可读性
const xhr = this;
if (arguments[1] == "xxxxx") {
// 获取response的操作符,
// 如果想要劫持responseText就需要改为responseText
// getOwnPropertyDescriptor获取的是一个对象上属性的相关的描述符
// 可以拿到get,set,enumerable,configurable等。
const getter = Object.getOwnPropertyDescriptor(
XMLHttpRequest.prototype,
"response"
).get;
// 这里对xhr的response属性做了一个defineProperty
// 当触发xhr.response的时候会访问对应的get
// 而get调用我们之前获取的getter函数,需要注意修改this指向
// 获取原返回内容,进行一定的修改,返回新的修改内容
Object.defineProperty(xhr, "response", {
get: () => {
let result = getter.call(xhr);
return result + " hook";
},
});
}
return xhrOpen.apply(xhr, arguments);
};

常见问题

为什么设置到 xhr 上而不是 xhr 的原型链上

因为这样可以形成对某一个 xhr 进行劫持,如果设置到原型链上相当于对所有的 xhr 进行了一次劫持,如果你的劫持 get 内有大量的回调代码会严重拖慢运行的速度,并且 response 以及 responseText 只有 get 函数而没有 set 函数,是不允许重定义的。

为什么我没有对原型链的 response 进行劫持,却成功劫持了?

因为查找 response 的属性的时候是按顺序查找的,首先在 xhr 上查找是否有 response,然后在 xhrproto 也就是构造函数的 prototype 上查找 response 属性,如果找不到就一层一层往上找,这里我们进行了劫持之后会直接在 xhr 自身找到,甚至不会找到 xhrproto 上的 response,所以实现了劫持的操作。

为什么我无法进行劫持

可能网页使用了 fetch 通信,或注入时机过晚

本节对应实战

抖音短视频无水印下载