setTimeout() vs ハッカー、仁義なき戦いによると
function isNativeFunction(func, name) { for (var o in func) { if (o === "toString") return false; } var match = func.toString().match(/^function (\S+)\(\)\s*{\s*\[native code\]\s*}$/); return (match && match[1] === name); } setInterval = function(){}; isNativeFunction(setInterval, 'setInterval'); // false
でsetIntervalが偽装されているか調べられると書いてあるが、そんなことはない。
自分が普段使っているブラウザはSafariなので他のブラウザではsetIntervalが上書きできないから下記コードは無効だが、setIntervalを書き換えたいと思う時など特定のときしかありえないので、Safariを使えばいいと思う
ほとんど全てのチェックを素通りするフェイクのsetIntervalの書き方をここに載せる(ネイティブかどうかを判別する方法を発見したら教えて欲しい)。ここではsetIntervalだけ上書きしているが、少しコードを追加すればsetTimeoutも上書きできる。
このコードを使うと、具体的には、setInterval(f, 1000);とすると、setInterval(f, 1)となり、スクリプト等でdocument-on時に走らせると全てが爆速になる。
(function () { var nativeSetInterval = setInterval; var nativeToString = Function.prototype.toString; // ネイティブはfunction () {であるがsetInterval.length = 1で、Object.definePropertyで上書き不可能なので仮の引数を入れてごまかす // この関数のtoString()については後から偽装するので大丈夫 var fakeSetInterval = function setInterval(_) { var fn = arguments[0]; var ms = arguments[1]; return nativeSetInterval.call(this, fn, ms / 1000); } // setInterval.toString()の結果を偽装 DontEnum等もネイティブ同様になるようにする Object.defineProperty(Function.prototype, 'toString', { configurable: true, enumerable: false, value: function toString() { if (this == fakeSetInterval) { return 'function setInterval() {\n [native code]\n}'; } if (this == Function.prototype) { // Function.prototype.toString()対策 return 'function () {\n [native code]\n}'; } // Function.prototype.toString.toString()対策 if (this == Function.prototype.toString) { return 'function toString() {\n [native code]\n}'; } // 偽装する必要のないものは元のtoStringを呼ぶ return nativeToString.apply(this, arguments); }, writable: true }); // 改造済みのsetIntervalを注入 Object.defineProperty(Window.prototype, 'setInterval', { configurable: true, enumerable: true, value: fakeSetInterval, writable: true }); })();
このコードを実行すると冒頭で引用したisNativeFunction(setInterval, 'setInterval')
がtrueを返す。唯一考えられる対策はiframeなどの外部ページからFunction.prototype.toString
を取ってきてそれを使うことだが、
var toString = iframe.contentWindow.Function.prototype.toString; toString.call(setInterval) == 'function setInterval() {\n [native code]\n}';
このコードもiframe内で、ユーザスクリプトでdocument-start時にFunction.prototype.toStringを書き換えてしまえばいいので、意味がない。クライアント側で実行するコードは、全てユーザのコントロール下にあると思って書かないと、脆弱性の原因になる。ハッカーの勝利である。
引用元のサイトにあるように、ダウンロードサイトの待ち時間やゲームなどで、どうしても時間によるチェックをいれたい場合、サーバ側でタイマーを使って、待ち時間が経過するまではAPIがエラーを返すように作るしかないと思う。