ファントムボットの定義

»翻訳記事PhantomJSベースの訪問者の検出 | ハッカーニュースに関する記事の良い議論



記事は古く、トマトを投げないでください-コメントであなたの経験を共有してください。



今日、多くのセキュリティインシデントは(悪意のあるユーザーによる)自動化を使用しています。 Webスクレイピング、パスワードの再利用、クリック詐欺-これはすべて、サイバー犯罪者が通常のユーザーになりすまそうとする試み(多くの場合成功)で行われます。つまり、本質的にはサーバーの通常のユーザーブラウザーのように見えます。 サイトの所有者は、おそらく魂のない鉄片ではなく人々にサービスを提供していることを確認したいかもしれませんが、サービスプロバイダーとしては、重くてバグのあるWebベースのインターフェイスではなく、APIを介してコンテンツへのアクセスを許可したいでしょう。



すでにcUrlおよび同様の訪問者の簡単なチェックがあり、非常に効果的であるとします。 次のステップは、クライアントが本物であり、 PhantomJSSlimerJSなどのクラフトのボットではなく、ダムでバグのあるUIを備えた本物のブラウザーを使用していることを確認することです



この記事では、ファントムボットを識別するためのいくつかのトリックを見ていきます。 ファントムの方が人気があるので、ファントムのみを検討しますが、SlimerJSなどには多くのポイントを使用できます。



重要! 検討された方法は、特に断りのない限り、両方のファントムブランチ(1.xおよび2.x)に適用できます。



そもそも、ファントムに応答することなく(つまり、http要求によってのみ)ファントムを識別することは可能ですか?



HTTPスタック



ファントムはQTフレームワーク上に構築されていることを知っておく必要があります。 そのため、Qtは他の最新のブラウザーとは少し異なる方法でHTTPスタックを実装します。



まず、Chromeの単純なhttpリクエストを見てみましょう。



GET / HTTP/1.1 Host: localhost:1337 Connection: keep-alive Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8 User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 Accept-Encoding: gzip, deflate, sdch Accept-Language: en-US,en;q=0.8,ru;q=0.6
      
      





そして今、幻で同じリクエスト:



 GET / HTTP/1.1 User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X) AppleWebKit/534.34 (KHTML, like Gecko) PhantomJS/1.9.8 Safari/534.34 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Connection: Keep-Alive Accept-Encoding: gzip Accept-Language: en-US,* Host: localhost:1337
      
      





ファントムヘッダーはクロムとは異なることに注意してください(ほとんどの最新のブラウザーと同様)。





これらのサーバー側ヘッダーの違いを確認すると、ファントムアクセスを判断するのに役立ちます。



しかし、そのようなチェックを信頼するのは安全ですか? 攻撃者がプロキシを使用してこれらのヘッダーを書き換える場合、一般的に、通常のブラウザを模倣することは難しくありません。



このような問題の解決策は、特効薬ではないようです。 さて、JavaScript環境のファントム機能を使用してクライアントでできることを見てみましょう。



クライアントでUser-Agentを確認する



User-Agentがリクエストで受け取ったとは思わないかもしれませんが、クライアントの値はどうですか?



 if (/PhantomJS/.test(window.navigator.userAgent)) { console.log("PhantomJS environment detected."); }
      
      





残念ながら、リクエストのヘッダーと同じくらい簡単に変更できるため、これでは明らかに不十分です。



プラグイン



navigator.pluginsには、ブラウザーにインストールされたプラグインの配列が含まれています。 通常、Flash、ActiveX、javaアプレットのサポート、またはこのブラウザーがOS Xでデフォルトになっていることを示すDefault Browser Helperのようなものが含まれています。携帯電話でも。



これがPhantomJsを区別するものです-プラグインをインストールせず、さらにプラグインをインストールする機会を与えません( PhantomJS API )。



次のチェックは非常に便利です。



 if (!(navigator.plugins instanceof PluginArray) || navigator.plugins.length == 0) { console.log("PhantomJS environment detected."); } else { console.log("PhantomJS environment not detected."); }
      
      





一方、ページをロードする前にjsコードを実行することでnavigator.plugins配列を置き換えることは非常に簡単です( こちらを参照 )。



また、これらのプラグインがインストールされたカスタムアセンブリを作成することは難しくありません。 これは、ファントムが構築されるQTがnpapiプラグインを接続する機能を提供するため、見た目よりもはるかに簡単です。



タイミング



もう1つの興味深い点は、PhantomJSがJavaScriptダイアログをハックする方法です。



 var start = Date.now(); alert('Press OK'); var elapse = Date.now() - start; if (elapse < 15) { console.log("PhantomJS environment detected. #1"); } else { console.log("PhantomJS environment not detected."); }
      
      





いくつかのチェックを行った後、ダイアログが15ミリ秒未満で閉じた場合、ブラウザは人によって制御されていない可能性が高いと想定できます。 しかし、この手法の使用には、あいまいなウィンドウを閉じることを余儀なくされる実際のユーザー側の否定的な部分が含まれます。 (実際、この点は、ユーザーのアクションにアタッチすることで回避できます。たとえば、任意の要素にカーソルを合わせたときに何かを提供するなどです。ユーザーが「いいえ、ありがとう」と言った瞬間です。ユーザーの観点から何が起こっているのか-およその翻訳。)



グローバル



PhantomJS 1.xは、2種類のグローバルを提供します。



 if (window.callPhantom || window._phantom) { console.log("PhantomJS environment detected."); } else { console.log("PhantomJS environment not detected."); }
      
      





ただし、これは実験技術の一部であるため、変更される可能性があります。



JavaScriptエンジンチップ



PhantomJS 1.xおよび2.xは、WebKitの最新バージョンを使用していません。これは、ブラウザの最新バージョンにすでに導入されている新しいファッショナブルなバンがないことを意味します。 これは自動的にJSエンジンに適用されます。つまり、一部のプロパティとメソッドは異なる動作をするか、PhantomJSに完全に存在しません(ここでは、古いブラウザーとの違い(Transl。程度)は実際には明確ではありません)



これらのメソッドの1つはFunction.prototype.bindで、PhantomJS 1.x以降にはありません。 次の例では、関数プロトタイプにバインドがあるかどうかを確認します。バインドがある場合は、確実にネイティブであり、ジェイルブレイクされていないかどうかを確認します。



 (function () { if (!Function.prototype.bind) { console.log("PhantomJS environment detected. #1"); return; } if (Function.prototype.bind.toString().replace(/bind/g, 'Error') != Error.toString()) { console.log("PhantomJS environment detected. #2"); return; } if (Function.prototype.toString.toString().replace(/toString/g, 'Error') != Error.toString()) { console.log("PhantomJS environment detected. #3"); return; } console.log("PhantomJS environment not detected."); })(); </script>
      
      





このコードが少し理解できないと思われる場合は、 ここ(ビデオ)で詳細な説明をご覧ください



スタックトレース



evaluateコマンドを介してPhantomJSによって処理されたJavaScriptコードによって生成されたエラーには、「ヘッドレス」ブラウザーを判別できる一意のスタックが含まれています。



PhantomJSが次のコードで処理を呼び出すとします。



 var err; try { null[0](); } catch (e) { err = e; } if (indexOfString(err.stack, 'phantomjs') > -1) { console.log("PhantomJS environment detected."); } else { console.log("PhantomJS environment is not detected."); }
      
      





ここでは、ネイティブのString.prototype.indexOfをPhantomJS(ユーザースクリプト)に置き換えて否定的な結果を返すことができるため、カスタムindexOfString()関数があることに注意してください(読者が実装に困難を感じないことを前提に、括弧内に実装を残しました)。 (一般に、確認も簡単です-およその翻訳。)



それでは、PhantomJSでこのコードを実行するにはどうすればよいでしょうか? 1つの手法は、呼び出される可能性が最も高い、最も一般的に使用されるDOM関数を書き換えることです。 たとえば、次のコードはdocument.querySelectorAllを書き換えて、ブラウザのスタックトレースをインターセプトします。



 var html = document.querySelectorAll('html'); var oldQSA = document.querySelectorAll; Document.prototype.querySelectorAll = Element.prototype.querySelectorAll = function () { var err; try { null[0](); } catch (e) { err = e; } if (indexOfString(err.stack, 'phantomjs') > -1) { return html; } else { return oldQSA.apply(this, arguments); } };
      
      





合計



この記事では、サーバーとクライアントの両方でPhantomJSを定義するための7つの異なる手法を検討しました。 テストの結果とフィードバックを組み合わせることで(たとえば、レンダリング速度の測定やセッションCookieの破壊など)、原則として、PhantomJSを訪問者にとって複雑にすることができます。 ただし、これらの手法は厳密でエラーがないわけではないことに注意してください(実際、カスタムアセンブリでは動作しません-およその翻訳です)。 トピックを深くするために、 プレゼンテーション(ビデオ、スライド)を表示することをお勧めします。 GitHubカブには 、例とチェックをバイパスする方法が記載されています。



ご清聴ありがとうございました!



All Articles