近年、多くのWebセキュリティインシデントに自動化が関与しています。Webスクレイピング、パスワードの使い回し、クリック詐欺などの攻撃では、攻撃者が実際のユーザーを装い、ブラウザからのアクセスに見せかけようとします。Webサイトの所有者は、人間のユーザーに確実にサービスを提供したいと考えます。また、Webサービスプロバイダーは、負荷が高く安定性に劣るWebインターフェースからコンテンツをスクレイピングされるのではなく、APIを介してプログラムからアクセスしてもらいたいと考えます。
cURLのようなアクセス元に対する基本的なチェックが実装済みであることを前提とすると、次の妥当なステップは、アクセス元がPhantomJSやSlimerJSのようなヘッドレスブラウザではなく、実際のUIで操作されるブラウザを使用していることを確認することです。
この記事では、PhantomJSによるアクセスを識別するための手法をいくつか紹介します。最も広く利用されているヘッドレスブラウザ環境であることからPhantomJSを取り上げますが、ここで説明する概念の多くはSlimerJSやその他のツールにも適用できます。
注:特に明記されていない限り、この記事で紹介する手法はPhantomJS 1.xと2.xの両方に適用できます。まず、PhantomJSに応答することなく検出することは可能でしょうか?
HTTPスタック
ご存じのとおり、PhantomJSはQt frameworkを基盤として構築されています。QtのHTTPスタックの実装方法は、他の最新ブラウザーとは一線を画しています。
まず、次のヘッダーを送信するChromeを見てみましょう。

一方、PhantomJSでは、同じHTTPリクエストは次のようになります。

PhantomJSのヘッダーは、いくつかの微妙な点でChrome(そして実際には、ほかのすべての最新ブラウザー)のヘッダーとは異なります。
- Hostヘッダーが最後に表示される
- Connectionヘッダーの値に大文字と小文字が混在している
- Accept-Encodingの値はgzipのみ
- User-Agentに「PhantomJS」が含まれている
サーバー上でこれらのHTTPヘッダーの異常を確認することで、PhantomJSブラウザーを識別できると考えられます。
しかし、これらの値を信用してもよいのでしょうか。攻撃者がヘッドレスブラウザの前段にプロキシを配置してヘッダーを書き換えれば、通常の最新ブラウザからのアクセスに見えるように変更できます。
この問題にサーバー側だけで対処しても、万能な解決策にはならないようです。そこで、PhantomJSのJavaScript環境を使用して、クライアント側で何ができるかを見てみましょう。
クライアント側でのUser-Agentチェック
HTTP経由で送信されるUser-Agent値は信頼できない可能性がありますが、クライアント側ではどうでしょうか。

残念ながら、PhantomJSではuser-agentヘッダーとnavigator.userAgentの値も同様に簡単に変更できるため、これだけでは不十分な可能性があります。
プラグイン
navigator.pluginsには、ブラウザ内に存在するプラグインの配列が格納されています。一般的なプラグインの値には、Flash、ActiveX、Javaアプレットのサポート、および「Default Browser Helper」があります。Default Browser Helperは、そのブラウザがOS Xのデフォルトブラウザであるかどうかを示すプラグインです。F5の調査では、一般的なブラウザを新規インストールした場合、モバイル環境であっても、ほとんどの場合、少なくとも1つのデフォルトプラグインが含まれています。
これは、プラグインが一切実装されておらず、追加する方法も提供されていないPhantomJSとは異なります(PhantomJS APIを使用した場合も同様です)。
その場合、次のチェックが役立つ可能性があります。

一方、PhantomJSのJavaScript環境をページが読み込まれる前に変更すれば、このプラグイン配列は比較的簡単に偽装できます。
実際に実装されたプラグインを備えたPhantomJSのカスタムビルドも、十分に考えられます。PhantomJSの基盤であるQtフレームワークには、プラグインを実装するためのネイティブAPIが用意されているため、実現は想像するほど難しくありません。
タイミング
もう1つ注目すべき点は、PhantomJSがJavaScriptダイアログを抑制する仕組みです。

複数回測定した結果、アラートダイアログが15ミリ秒以内に抑制された場合、そのブラウザは人間によって操作されていない可能性が高いと考えられます。ただし、この方法では実際のユーザーにもアラートが表示され、手動で閉じてもらう必要があるため、ユーザーに負担をかけることになります。
グローバルプロパティ
PhantomJS 1.xでは、グローバルオブジェクトに2つのプロパティが公開されています。

ただし、これらのプロパティは試験的機能の一部であり、今後変更される可能性があります。
JavaScriptエンジン機能の不足
PhantomJS 1.xおよび2.xでは現在、古いWebKitエンジンが使用されているため、新しいブラウザーには存在してもPhantomJSには存在しないブラウザー機能があります。これはJavaScriptエンジンにも当てはまり、一部のネイティブプロパティやメソッドがPhantomJSでは異なるか、存在しません。
そのようなメソッドの1つがFunction.prototype.bindです。これはPhantomJS 1.x以前には存在しません。次の例では、bindが存在するかどうか、および実行環境で偽装されていないかどうかを確認します。

このコードは少し複雑なため、ここでは詳しい説明を省きます。詳細については、プレゼンテーションをご覧ください。
スタックトレース
PhantomJSがevaluateコマンドを介して評価したJavaScriptコードで発生するエラーには、一意に識別可能なスタックトレースが含まれているため、ヘッドレスブラウザを特定できます。
たとえば、PhantomJSが次のコードに対してevaluateを呼び出すとします。

この例では、カスタムの indexOfString() 関数を使用しています。この関数の実装は読者への演習として省略しています。これは、ネイティブの String.prototype.indexOf がPhantomJSによって偽装され、常に負の値を返す可能性があるためです。
では、PhantomJSスクリプトにこのコードを評価させるにはどうすればよいでしょうか。1つの方法は、呼び出される可能性が高い、よく使用されるDOM API関数の一部をオーバーライドすることです。たとえば、以下のコードではdocument.querySelectorAllをオーバーライドして、ブラウザーのスタックトレースを調べます。

まとめ
この記事では、サーバー上での識別と、PhantomJSのクライアントJavaScript環境でのコード実行による識別の両面から、PhantomJSを識別する7つの手法を紹介しました。検出結果を、動的ページを機能しない状態にする、現在のセッションCookieを無効にするといった強力な対処メカニズムと組み合わせることで、PhantomJSによるアクセスに対して大きな障壁を設けることができます。ただし、これらの手法は万全ではなく、高度な攻撃者には最終的に突破される可能性があることを常に念頭に置いてください。
詳細については、こちらの録画をご覧になることをお勧めします:AppSec USA 2014でのプレゼンテーション(スライド)。また、ここで紹介した手法の実装例と、想定される回避方法をまとめたGitHubリポジトリも用意しています。
お読みいただきありがとうございました。それでは、よいハンティングを。
寄稿者
Sergey Shekyan – @sshekyan
Ben Vinegar – @bentlegen
Bei Zhang – @ikarienator
著者について
関連するブログ記事

F5 Application Study Tool:OpenTelemetryによるF5 BIG-IP環境の監視
F5の新しいオープンソースツールは、BIG-IP環境を詳細に可視化し、データを追跡するとともに、強力でカスタマイズ可能なインサイトを得るための基盤を提供します。
F5 BIG-IPでAI防御を実現
F5 BIG-IP Advanced WAF、BIG-IP APM、BIG-IP SSL Orchestratorが、生成AIアプリ向けに包括的なセキュリティソリューションを提供する仕組みをご覧ください。
F5 Advanced WAFによるGraphQL APIの保護
ここ数年、多くの組織がデジタルトランスフォーメーションを進めており、COVID-19のパンデミックによってその動きは加速しています。
アプリケーションサービスの現状 通信業界版:リーダーとしての台頭
通信ネットワークが進化し、さまざまな業界の幅広いユースケースに対応するなか、通信業界の回答者がテクノロジーリーダーとしての存在感を高めていることがわかります。
デジタルトランスフォーメーションは、単なるペーパーレス化ではない
デジタルトランスフォーメーションは、単なるペーパーレス化ではありません。真のモダナイゼーションにアプリケーションアーキテクチャとセキュリティの再構築が必要な理由を、F5ブログでご覧ください。
やめましょう。もう本当にやめましょう。HTTPSはHTTPより高速ではありません。
もうやめましょう。HTTPSはHTTPより高速ではありません。プロトコルのパフォーマンスに関する微妙な違いと、暗号化トラフィックを適切に最適化する方法については、F5ブログをご覧ください。