【发布时间】:2021-10-22 09:09:48
【问题描述】:
我正在尝试捕获对Storage 的调用。据我所知,有两种方法可以调用setItem 或getItem:
sessionStorage.setItem("foo", "bar");
let item = sessionStorage.getItem("foo");
Storage.prototype.setItem.call(sessionStorage, "foo", "bar");
let item2 = Storage.prototype.getItem.call(sessionStorage, "foo");
在代码片段中使用sessionStorage 会引发安全错误,因此here's the code in JS Fiddle
TLDR:如果我这样做,我可以处理所有情况,但它看起来很老套。有没有更好/更清洁的方法来实现我的目标? (注意:我无法控制调用者,这就是我涵盖这两种情况的原因):
try {
console.log("\n\n");
let ss = new Proxy(sessionStorage, {
get: function (getTarget, p) {
if (p === "__this") {
// kinda hacky, but allows us to unwrap Proxy for binding
return getTarget;
}
console.log("sessionStorage.get proxy called")
return new Proxy(Reflect.get(getTarget, p), {
apply(applyTarget, thisArg, argArray) {
console.log("sessionStorage.get.apply called");
Reflect.apply(applyTarget, getTarget, argArray);
}
})
},
});
SP = new Proxy(Object.create(Storage.prototype), {
get: function (getTarget, p) {
console.log("Storage.get proxy called")
return new Proxy(Reflect.get(getTarget, p), {
apply(applyTarget, thisArg, argArray) {
console.log("Storage.get.apply called");
try {
return Reflect.apply(applyTarget, thisArg, argArray);
} catch (e) {
// unpack proxy if we're double-wrapped (both target and thisArg are Proxy)
return Reflect.apply(applyTarget, thisArg.__this, argArray);
}
}
})
},
});
SPW = {};
Object.defineProperty(SPW, 'prototype', {
value: SP,
configurable: false,
});
si = SPW.prototype.setItem;
gi = SPW.prototype.getItem;
si.call(ss, "foo", "3");
console.log(`Storage.prototype.getItem: ${gi.call(ss, "foo")}`);
console.log(`Storage.prototype Worked`)
} catch (e) {
console.log(`Storage.prototype Caught ${e.stack}`);
}
结果:
Storage.get proxy called
Storage.get proxy called
Storage.get.apply called
Storage.get.apply called
Storage.prototype.getItem: 3
Storage.prototype Worked
除了包含“隐藏”__this 属性之外,我没有看到任何其他方法,以便调用者可以“解包”Proxy 对象并获取对原始sessionStorage 的引用。有没有更好的方法来做到这一点?
背景
如果有帮助,这里是仅包装 sessionStorage 或 Storage 的示例,但不能同时包装两者:
换行sessionStorage
对于apply 陷阱,我必须传递getTarget 而不是thisArg,因为后者是 Proxy 对象,如果我传递它,则会引发Illegal Invocation 错误。
try {
let ss = new Proxy(sessionStorage, {
get: function (getTarget, p) {
console.log("sessionStorage.get called")
return new Proxy(Reflect.get(getTarget, p), {
apply(applyTarget, thisArg, argArray) {
console.log("sessionStorage.get.apply called");
Reflect.apply(applyTarget, getTarget, argArray);
}
})
},
});
ss.setItem("foo", "1");
console.log(`Proxy.sessionStorage.getItem: ${ss.getItem("foo")}`);
console.log(`Proxy.sessionStorage Worked`)
} catch (e) {
console.log(`Proxy.sessionStorage Caught ${e.stack}`);
}
结果:
sessionStorage.get called
sessionStorage.get.apply called
sessionStorage.get called
sessionStorage.get.apply called
sessionStorage.get called
sessionStorage.get.apply called
Proxy.sessionStorage.getItem: undefined
Proxy.sessionStorage Worked
换行Storage.prototype
在这里,我必须首先使用单独的prototype 创建一个新对象,因为Storage 的prototype 属性描述符将configurable 设置为false。完成此操作后,我基本上必须通过传递“未包装”getTarget 而不是thisArg 指向的Proxy 实例来做与前一个案例相同的事情。
try {
console.log("\n\n");
SP = new Proxy(Object.create(Storage.prototype), {
get: function (getTarget, p) {
console.log("Storage.get proxy called")
return new Proxy(Reflect.get(getTarget, p), {
apply(applyTarget, thisArg, argArray) {
console.log("Storage.get.apply called");
try {
return Reflect.apply(applyTarget, thisArg, argArray);
} catch (e) {
console.log("apply failed when passing thisArg");
return Reflect.apply(applyTarget, getTarget, argArray);
}
}
})
},
});
SPW = {};
Object.defineProperty(SPW, 'prototype', {
value: SP,
configurable: false,
});
si = SPW.prototype.setItem;
gi = SPW.prototype.getItem;
si.call(sessionStorage, "foo", "2");
console.log(`Storage.prototype.getItem: ${gi.call(sessionStorage, "foo")}`);
console.log(`Storage.prototype Worked`)
} catch (e) {
console.log(`Storage.prototype Caught ${e.stack}`);
}
结果:
Storage.get proxy called
Storage.get proxy called
Storage.get.apply called
Storage.get.apply called
Storage.prototype.getItem: 2
Storage.prototype Worked
【问题讨论】:
-
您试图拦截的调用和/或分配究竟是什么?可能你根本不应该使用
Proxy,它只会让一切变得更复杂。 -
我正在尝试拦截浏览器指纹脚本的调用。我的目的是记录他们并分析他们收集的数据。我看过的其他脚本无法完全记录所有收集的数据,这是指纹如何规避拦截操作尝试的一个示例:通过使用不可配置的对象原型。
-
能否请您添加一些示例浏览器指纹识别脚本所做的具体调用?它是只调用
localStorage.getItem()和localStorage.setItem(),还是以其他方式访问localStorage? -
@Bergi 在这种情况下让我搜索的具体示例使用了我在问题开始时演示的原型方法。虽然我的方法的部分原因是因为脚本通常被混淆,所以我试图通过拦截而不是逆向工程来观察它们的调用。因此,我不是在单个时间点解决单个网站,而是尝试开发一个广泛的解决方案,该解决方案可以观察任意脚本以及收集数据的变化以及收集方式的变化。
标签: javascript javascript-proxy