========================================================================== Root Link: Checking if a JavaScript native function is monkey patched https://mmazzarolo.com/blog/2022-07-30-checking-if-a-javascript-native-function-was-monkey-patched/ ========================================================================== ========================================================================== 2024-10-07: -------------------------------------------------------------------------- ("prototype" in $_document.write)の条件でdocument.writeが改ざんされたかチェックできると思っていたが間違いだった。 -------------------------------------------------------------------------- 改ざん前のdocument.writeはnative functionであり、"prototype"を持たず、 document.writeが改ざんされれば、"prototype"を持つと考えていた。 しかし、事実は、native functionのように、"prototype"を持たないfunctionが 存在するのである。以下に列記する。 (1) Arrow Functions: (e.g.) document.write=(str)=>{data=str;}; (2) Bound Functions: (e.g.) document.write=(function(str){data=str;}).bind(null); 従って、"prototype"プロパティの有無だけには頼れない。 改ざんを検出するには、 ========================================================================== ========================================================================== documentとその__proto__ (HTMLDocument)には'write'のプロパティは存在しない。 'write'のプロパティがあるのは、document.__proto__.__proto__ (Document)である。 -------------------------------------------------------------------------- document.hasOwnProperty("write") ==> false document.__proto__.hasOwnProperty("write") ==> false document.__proto__.__proto__.hasOwnProperty("write") ==> true -------------------------------------------------------------------------- これにより、もしdocumentとdocument.__proto__のwriteが改ざんされた場合、 hasOwnProperty("write")がtrueになり、すぐに気づく。 しかし、document.__proto__.__proto__.writeが改善されても、 hasOwnProperty("write")で判定はできない。 ========================================================================== ========================================================================== document.write.toString() は以下のストリングを返す。 -------------------------------------------------------------------------- function write() { [native code] } -------------------------------------------------------------------------- Bound Functionsも[native code]がはいったストリングを返す。 -------------------------------------------------------------------------- function () { [native code] } -------------------------------------------------------------------------- ========================================================================== ========================================================================== write プロパティを得るためには、[Document]までprototype chainを 遡る必要がある。 -------------------------------------------------------------------------- document.hasOwnProperty('write') ==> false document.__proto__.hasOwnProperty('write') ==> false [HTMLDocument] document.__proto__.__proto__.hasOwnProperty('write') ==> true [Document] -------------------------------------------------------------------------- toString プロパティを得るためには、[Function]までprototype chainを 下がる必要がある。その下の[Object]にもtoString プロパティはあるが、 これはdocument.writeには使われていない。 -------------------------------------------------------------------------- document.write.hasOwnProperty('toString') ==> false document.write.__proto__.hasOwnProperty('toString') ==> true [Function] document.write.__proto__.__proto__.hasOwnProperty('toString') ==> true [Object] ========================================================================== ========================================================================== Proxy -------------------------------------------------------------------------- [Method 1] document=new Proxy(document,{ get: function(target, prop, receiver) { if (prop === 'write') { return function(str) { console.log(str); .................... .................... .................... // Optionally, call the original method // target[prop].call(this, str); }; } return Reflect.get(target, prop, receiver); } }); Since it returns a regular function when (prop === 'write'), document.write will be a regular function. Detecting this type of Proxy is the same as detecting a regular function. -------------------------------------------------------------------------- [Method 2] document.__proto__.__proto__.write=new Proxy(document.write,{ apply: function (target, thisArg, argumentsList) { .................... .................... .................... return Reflect.apply(target, thisArg, argumentsList); } }); This one is not easy. The only difference between the original document.write and the proxied document write is the return of toString(); [Original Build-In] function~write()~{~[native~code]~} function~write()~{\n~~~~[native~code]\n} (FireFox) [Proxy] function~()~{~[native~code]~} function ProxyObject() { [native code] } (Safari 13.1.3) ==========================================================================