Chromedriverを使ってSeleniumをテストしていますが、自動化がまったく行われていなくても、Seleniumを使用していることを検出できるページがいくつかあることに気付きました。 SeleniumとXephyrを介してクロムを使用して手動でブラウズしているだけでも、疑わしい活動が検出されたというページが頻繁に表示されます。私は私のユーザーエージェントと私のブラウザの指紋をチェックしました、そしてそれらはすべて通常のクロムブラウザと全く同じです。
通常のクロムでこれらのサイトを閲覧すると、すべてうまくいくが、Seleniumを使用した瞬間に検出された。
理論的には、chromedriverとchromeは、どのWebサーバにとっても文字通りまったく同じに見えるはずですが、どういうわけか彼らはそれを検出することができます。
テストコードが欲しいなら、これを試してみてください。
from pyvirtualdisplay import Display
from Selenium import webdriver
display = Display(visible=1, size=(1600, 902))
display.start()
chrome_options = webdriver.ChromeOptions()
chrome_options.add_argument('--disable-extensions')
chrome_options.add_argument('--profile-directory=Default')
chrome_options.add_argument("--incognito")
chrome_options.add_argument("--disable-plugins-discovery");
chrome_options.add_argument("--start-maximized")
driver = webdriver.Chrome(chrome_options=chrome_options)
driver.delete_all_cookies()
driver.set_window_size(800,800)
driver.set_window_position(0,0)
print 'arguments done'
driver.get('http://stubhub.com')
Stubhubをブラウズすると、1つか2つのリクエスト内でリダイレクトされて「ブロック」されます。私はこれを調査しています、そして、ユーザーがSeleniumを使用していることを彼らがどのように知ることができるかについて私は理解することができません。
どうやってやっているの?
更新を編集します。
私はFirefoxにSelenium IDEプラグインをインストールしましたが、通常のFirefoxブラウザでstubhub.comにアクセスすると、追加のプラグインだけで禁止されました。
編集:
送受信されているHTTPリクエストを表示するためにFiddlerを使用すると、 '偽のブラウザ'リクエストのレスポンスヘッダに 'no-cache'が含まれていることに気付きました。
編集:
このような結果 JavascriptのSelenium Webdriverページにいることを検出する方法はありますか Webドライバを使用していることを検出する方法はないはずです。しかし、この証拠はそうでないことを示唆しています。
編集:
サイトは彼らのサーバーに指紋をアップロードする、しかし私がチェックした、そしてSeleniumの指紋はクロムを使うとき指紋と同一である。
編集:
これは、サーバーに送信するフィンガープリントペイロードの1つです。
{"appName":"Netscape","platform":"Linuxx86_64","cookies":1,"syslang":"en-US","userlang":"en-US","cpu":"","productSub":"20030107","setTimeout":1,"setInterval":1,"plugins":{"0":"ChromePDFViewer","1":"ShockwaveFlash","2":"WidevineContentDecryptionModule","3":"NativeClient","4":"ChromePDFViewer"},"mimeTypes":{"0":"application/pdf","1":"ShockwaveFlashapplication/x-shockwave-flash","2":"FutureSplashPlayerapplication/futuresplash","3":"WidevineContentDecryptionModuleapplication/x-ppapi-widevine-cdm","4":"NativeClientExecutableapplication/x-nacl","5":"PortableNativeClientExecutableapplication/x-pnacl","6":"PortableDocumentFormatapplication/x-google-chrome-pdf"},"screen":{"width":1600,"height":900,"colorDepth":24},"fonts":{"0":"monospace","1":"DejaVuSerif","2":"Georgia","3":"DejaVuSans","4":"TrebuchetMS","5":"Verdana","6":"AndaleMono","7":"DejaVuSansMono","8":"LiberationMono","9":"NimbusMonoL","10":"CourierNew","11":"Courier"}}
Seleniumとchromeでは同じ
編集:
VPNは使い捨てですが、最初のページを読み込んだ後に検出されます。明らかにいくつかのJavaScriptがSeleniumを検出するために実行されています。
VimまたはPerlを使用してcdc_
変数を置き換える
vim
を使用するか、@ Vic Seedoubleyewが@ Erti-Chris Eelmaaによる回答で指摘したように、Perl
を使用して、chromedriver
(-のcdc_
変数を置き換えることができます。 その変数の詳細については、@ Erti-Chris Eelmaaの投稿を参照)。 vim
またはPerl
を使用すると、ソースコードを再コンパイルしたり、16進エディタを使用したりする必要がなくなります。編集する前に、元のchromedriver
のコピーを作成してください。また、以下のメソッドはchromedriver version 2.41.578706
でテストされました。
vim /path/to/chromedriver
上記の行を実行すると、おそらく大量の意味不明な文字が表示されます。以下をせよ:
cdc_
と入力してreturn
を押して、/cdc_
を検索します。a
を押して編集を有効にします。$cdc_lasutopfhvcZLmcfl
をすべて削除し、削除されたものを同じ量の文字に置き換えます。そうしないと、chromedriver
は失敗します。esc
を押します。:wq!
と入力し、return
を押します。:q!
と入力してreturn
を押します。変更されたchromedriver
に移動し、ダブルクリックします。 terminal
ウィンドウが開きます。出力にkilled
が表示されない場合、ドライバーは正常に変更されています。
以下の行は、cdc_
をdog_
に置き換えます。
Perl -pi -e 's/cdc_/dog_/g' /path/to/chromedriver
置換文字列の文字数が検索文字列と同じであることを確認してください。そうでない場合、chromedriver
は失敗します。
Perlの説明
s///g
は、文字列を検索し、それを別の文字列でグローバルに置換することを示します(すべての出現を置き換えます)。
例:
s/string/replacment/g
そう、
s///
は、文字列の検索と置換を示します。
cdc_
は検索文字列です。
dog_
は置換文字列です。
g
は、文字列のすべての出現を置き換えるグローバルキーです。
Perlの置換が機能したかどうかを確認する方法
次の行は、検索文字列cdc_
のすべての出現を印刷します。
Perl -ne 'while(/cdc_/g){print "$&\n";}' /path/to/chromedriver
これが何も返さない場合、cdc_
は置き換えられています。
逆に、これを使用できます:
Perl -ne 'while(/dog_/g){print "$&\n";}' /path/to/chromedriver
置換文字列dog_
がchromedriver
バイナリにあるかどうかを確認します。そうである場合、置換文字列がコンソールに出力されます。
変更されたchromedriver
に移動し、ダブルクリックします。 terminal
ウィンドウが開きます。出力にkilled
が表示されない場合、ドライバーは正常に変更されています。
chromedriver
バイナリを変更した後、変更されたchromedriver
バイナリの名前がchromedriver
であり、元のバイナリが元の場所から移動されるか、名前が変更されることを確認します。
ログインしようとしたときにウェブサイトで以前に検出されていましたが、cdc_
を同じサイズの文字列に置き換えた後、ログインできました。この方法を使用した後でも、他のさまざまな理由でブロックされる可能性があります。そのため、VPN、別のネットワーク、またはあなたが持っているものを使用して、あなたを検出していたサイトにアクセスする必要があります。
基本的に、Selenium検出の動作方法は、Seleniumで実行するときに表示される事前定義されたjavascript変数をテストすることです。ボット検出スクリプトは通常、任意の変数(ウィンドウオブジェクト上)にWord "Selenium"/"webdriver"を含むものを探し、$cdc_
および$wdc_
という変数を文書化します。もちろん、これはすべて、どのブラウザーを使用しているかによって異なります。すべての異なるブラウザーは、異なるものを公開します。
私のために、私はクロムを使用したので、私がしなければならなかったすべては、$cdc_
がドキュメント変数としてもはや存在しないことを確認することでした。 $cdc_
を別の名前で再コンパイルします。)
これは私がchromedriverで変更した関数です:
call_function.js:
function getPageCache(opt_doc) {
var doc = opt_doc || document;
//var key = '$cdc_asdjflasutopfhvcZLmcfl_';
var key = 'randomblabla_';
if (!(key in doc))
doc[key] = new Cache();
return doc[key];
}
(コメントに注意してください、私がしたことはすべて$cdc_
をrandomblabla_
に変えました。
ボットネットワークが使用する可能性のある手法のいくつかを示す擬似コードを次に示します。
runBotDetection = function () {
var documentDetectionKeys = [
"__webdriver_evaluate",
"__Selenium_evaluate",
"__webdriver_script_function",
"__webdriver_script_func",
"__webdriver_script_fn",
"__fxdriver_evaluate",
"__driver_unwrapped",
"__webdriver_unwrapped",
"__driver_evaluate",
"__Selenium_unwrapped",
"__fxdriver_unwrapped",
];
var windowDetectionKeys = [
"_phantom",
"__nightmare",
"_Selenium",
"callPhantom",
"callSelenium",
"_Selenium_IDE_Recorder",
];
for (const windowDetectionKey in windowDetectionKeys) {
const windowDetectionKeyValue = windowDetectionKeys[windowDetectionKey];
if (window[windowDetectionKeyValue]) {
return true;
}
};
for (const documentDetectionKey in documentDetectionKeys) {
const documentDetectionKeyValue = documentDetectionKeys[documentDetectionKey];
if (window['document'][documentDetectionKeyValue]) {
return true;
}
};
for (const documentKey in window['document']) {
if (documentKey.match(/\$[a-z]dc_/) && window['document'][documentKey]['cache_']) {
return true;
}
}
if (window['external'] && window['external'].toString() && (window['external'].toString()['indexOf']('Sequentum') != -1)) return true;
if (window['document']['documentElement']['getAttribute']('Selenium')) return true;
if (window['document']['documentElement']['getAttribute']('webdriver')) return true;
if (window['document']['documentElement']['getAttribute']('driver')) return true;
return false;
};
ユーザー@szxによると、16進エディターでchromedriver.exeを開いて、実際にコンパイルを行わずに、手動で置換を実行することもできます。
質問と投稿された回答ですでに把握しているように、ここでは "Distil Networks" と呼ばれるアンチWebスクレイピングとBot検出サービスがあります。そして、会社のCEOの インタビュー によると:
新しいボットを作成することはできますが、Seleniumを彼らが使用しているツールとして識別するための方法を考え出したので、そのボットで何回繰り返してもSeleniumをブロックします _。私たちは今、Pythonとさまざまな技術を使ってそれを行っています。ある種類のボットからパターンが発生したことが確認されたら、次にそれらが使用するテクノロジをリバースエンジニアリングし、悪意のあるものとして識別するようにします。
Seleniumをどの程度正確に検出しているのかを理解するには時間と追加の課題がありますが、現時点で確実に言えることは何でしょうか。
明らかに次のように答えとして投稿することにしました。
SeleniumとChromedriverを同時に使用していることをWebサイトで検出できますか?
はい。
また、私が試したことがないのは、古いSeleniumと古いブラウザのバージョンです - 理論的には、Distil Networksのボットディテクタが現在依存しているある時点で、Seleniumに実装または追加されたものがあるかもしれません。それならば、どの時点/バージョンで適切な変更が行われたかを検出し(変更検出器を検出しましょう)、changelogとchangesetsを調べてください。それは彼らがウェブドライバを搭載したブラウザを検出するために何を使っているのか。テストする必要があるのは単なる理論です。
Wellsfargo.comへの実装例:
try {
if (window.document.documentElement.getAttribute("webdriver")) return !+[]
} catch (IDLMrxxel) {}
try {
if ("_Selenium_IDE_Recorder" in window) return !+""
} catch (KknKsUayS) {}
try {
if ("__webdriver_script_fn" in document) return !+""
partial interface Navigator { readonly attribute boolean webdriver; };
Navigatorインタフェースのwebdriver IDL属性はwebdriver-activeフラグの値を返さなければなりません。これは最初はfalseです。
このプロパティにより、WebサイトはユーザーエージェントがWebDriverによる制御下にあると判断し、サービス拒否攻撃を軽減するために使用できます。
2017 W3Cエディターズドラフトオブウェブドライバー から直接取得。これは、少なくとも、Seleniumのドライバーの将来の反復が誤用を防ぐために識別可能になることを強く示唆しています。結局のところ、ソースコードなしでは、chromeドライバが正確に検出可能になる原因を正確に知ることは困難です。
Seleniumをchromeという特定のユーザープロファイルで使用してみてください。そうすることで特定のユーザーとしてそれを使用し、必要なものを定義することができます。タグとの違いがわかります。
例えば:
username = os.getenv("USERNAME")
userProfile = "C:\\Users\\" + username + "\\AppData\\Local\\Google\\Chrome\\User Data\\Default"
options = webdriver.ChromeOptions()
options.add_argument("user-data-dir={}".format(userProfile))
# add here any tag you want.
options.add_experimental_option("excludeSwitches", ["ignore-certificate-errors", "safebrowsing-disable-download-protection", "safebrowsing-disable-auto-update", "disable-client-side-phishing-detection"])
chromedriver = "C:\Python27\chromedriver\chromedriver.exe"
os.environ["webdriver.chrome.driver"] = chromedriver
browser = webdriver.Chrome(executable_path=chromedriver, chrome_options=options)
クロムタグリスト ここ
私はクロムドライバーのソースコードをチェックしました。それはブラウザにいくつかのJavaScriptファイルを挿入します。
このリンク上のすべてのJavaScriptファイルがWebページに挿入されます。 https://chromium.googlesource.com/chromium/src/+/master/chrome/test/chromedriver/ js/
そこで私はリバースエンジニアリングと 難読化 jsファイルを16進数編集で使いました。これで、Seleniumのアクティビティを明らかにするために、これ以上のJavaScript変数、関数名、および固定文字列が使用されなくなりました。しかし、まだいくつかのサイトとreCaptchaはSeleniumを検出します。
おそらく、彼らはchromedriver jsの実行によって引き起こされた変更をチェックしています:)
編集1:
私は一時的にchromedriverの使用法を発見する 'navigator'にいくつかのパラメータがあることを発見しました。これらはパラメータです:
だから私が必要としていたのは、Webページ上でJavaScriptを実行するためのクロムの拡張機能でした。この記事で提供されている jsコード を使用して拡張機能を作成し、 別の記事 を使用して私のプロジェクトにzip形式の拡張機能を追加しました。 値を正しく変更しました。しかし、まだ何も変わっていません!
私はこれらのような他の変数を見つけませんでした、しかしそれはそれらが存在しないという意味ではありません。それでもreCaptchaはクロームドライバーを検出するので、変更する変数はもっとあるはずです。 次のステップ は、私がしたくない検出器サービスのリバースエンジニアリングであるべきです。
今 この自動化プロセスにもっと時間をかけたり、他の方法を探したりする価値があるかどうかわかりません。
正しいデータをすべて送信している場合でも(たとえば、Seleniumが拡張機能として表示されない場合は、妥当な解像度/ビット深度などがあります)、訪問者の行動をプロファイルするサービスやツールが多数あります。アクターはユーザーまたは自動化システムです。
たとえば、サイトにアクセスした後すぐにマウスを関連するボタンに直接移動することによって何らかのアクションを実行しようとすると、1秒もかかりませんが、ユーザーが実際に行うことはありません。
また、 https://panopticlick.eff.org/ などのサイトを使用してブラウザのユニーク性を確認することも、デバッグツールとして役立ちます。 Seleniumで実行していることを示す特定のパラメータがあるかどうかを確認するのにも役立ちます。
Webアプリケーションのファイアウォールの内側にいるようです。 modsecurityとowaspを見て、それらがどのように機能するのかを確かめてください。実際には、ボット検出回避をどのように行うかということです。それはSelenium Webドライバが目的としているものではありません。それはあなたのWebアプリケーションが他のWebアプリケーションを打たないようにテストするためのものです。それは可能ですが、基本的には、WAFがそのルールセットで探すものを調べ、可能であればSeleniumでそれを避ける必要があります。それでも、どのWAFを使用しているのかわからないため、まだ機能しない可能性があります。あなたは正しい第一歩を踏み出しました、それはユーザエージェントを偽造することです。それでもうまくいかない場合は、WAFが用意されているので、おそらくもっとトリッキーになる必要があります。
編集:他の答えから取られたポイント。まずあなたのユーザーエージェントが実際に正しく設定されていることを確認してください。ローカルWebサーバーにアクセスするか、トラフィックが傍受される可能性があります。
Webドライバを使用している場合、Firefoxはwindow.navigator.webdriver === true
を設定すると言われています。それは、古い仕様の1つ(例: archive.org )に従っていましたが、 新しいものには見つかりませんでした 付録の非常に曖昧な表現を除いて。
そのためのテストはファイルのSeleniumコードにあります fingerprint_test.js 最後のコメントは "現在はFirefoxでのみ実装されています"と書いていますが、私はいくつかの単純なgrep
ingでその方向のコードを識別できませんでした現在の(41.0.2)FirefoxリリースツリーにもChromiumツリーにもありません。
2015年1月からのFirefoxドライバ b82512999938でのフィンガープリントに関するより古いコミットへのコメントも見つけました 。そのコードは、昨日javascript/firefox-driver/extension/content/server.js
にダウンロードされたSelenium GITマスターの中にまだ残っています。コメントは、現在のw3c Webドライバーの仕様の中で少し異なる名前の付いた付録にリンクしています。
私が見たボット検出は、私が以下の答えで読んだものと比べてより洗練されているか、少なくとも異なるようです。
実験1:
実験2:
以前と同様に、PythonコンソールからSeleniumを使用してブラウザとWebページを開きます。
今回は、マウスでクリックする代わりに、(Pythonコンソールの)Seleniumを使用して、ランダムなオフセットで同じ要素をクリックします。
リンクは開かないが、私はサインアップページに連れて行かれる。
含意:
不思議に思えるかもしれませんが、アクションがSeleniumから発生したものかどうかを判断できるのではなく、ブラウザ自体がSeleniumを介して開かれたかどうかは関係ありません。それとも、ウィンドウにフォーカスがあるかどうかを判断できますか?誰かが何か洞察を持っているならば、聞くのはおもしろいでしょう。
いくつかのサイトはこれを検出しています:
function d() {
try {
if (window.document.$cdc_asdjflasutopfhvcZLmcfl_.cache_)
return !0
} catch (e) {}
try {
//if (window.document.documentElement.getAttribute(decodeURIComponent("%77%65%62%64%72%69%76%65%72")))
if (window.document.documentElement.getAttribute("webdriver"))
return !0
} catch (e) {}
try {
//if (decodeURIComponent("%5F%53%65%6C%65%6E%69%75%6D%5F%49%44%45%5F%52%65%63%6F%72%64%65%72") in window)
if ("_Selenium_IDE_Recorder" in window)
return !0
} catch (e) {}
try {
//if (decodeURIComponent("%5F%5F%77%65%62%64%72%69%76%65%72%5F%73%63%72%69%70%74%5F%66%6E") in document)
if ("__webdriver_script_fn" in document)
return !0
} catch (e) {}
次のコードでHTMLページを書きます。 DOMでは、SeleniumがouterHTMLのwebdriver属性を適用していることがわかります。
<html>
<head>
<script type="text/javascript">
<!--
function showWindow(){
javascript:(alert(document.documentElement.outerHTML));
}
//-->
</script>
</head>
<body>
<form>
<input type="button" value="Show outerHTML" onclick="showWindow()">
</form>
</body>
</html>
私が見つけたもう一つのことは、いくつかのウェブサイトがユーザーエージェントをチェックするプラットフォームを使っているということです。値に "HeadlessChrome"が含まれていると、ヘッドレスモードを使用したときの動作がおかしくなります。
これを回避するには、たとえばJavaでユーザーエージェントの値を上書きします。chromeOptions.addArguments( " - user-agent = Mozilla/5.0(Macintosh; Intel Mac OS X 10_13_6)AppleWebKit/537.36(GeckoのようなKHTML) Chrome/73.0.3683.86 Safari/537.36 ");
@ Erti-Chris Eelmaaのすばらしい答えに加えて - 迷惑なwindow.navigator.webdriver
があり、それは読み取り専用です。イベントの値をfalse
に変更した場合でもイベントにはtrue
が含まれます。自動化されたソフトウェアによって駆動されているブラウザがまだ検出される理由はありません。 _ mdn _
変数は、クロムのフラグ--enable-automation
によって管理されます。 chromedriverはそのフラグでchromeを起動し、chromeはwindow.navigator.webdriver
をtrue
に設定します。あなたはそれを見つけることができます ここ 。 「除外スイッチ」にフラグを追加する必要があります。例えば(golang):
package main
import (
"github.com/tebeka/Selenium"
"github.com/tebeka/Selenium/chrome"
)
func main() {
caps := Selenium.Capabilities{
"browserName": "chrome",
}
chromeCaps := chrome.Capabilities{
Path: "/path/to/chrome-binary",
ExcludeSwitches: []string{"enable-automation"},
}
caps.AddChrome(chromeCaps)
wd, err := Selenium.NewRemote(caps, fmt.Sprintf("http://localhost:%d/wd/hub", 4444))
}
私にとってSeleniumでそれをする最も簡単な方法はブラウザのフィンガープリントを送り返すXHRを傍受することです。
しかし、これはSeleniumのみの問題なので、他のものを使用することをお勧めします。 Seleniumは、このようなことをより簡単にするためのものです。
私はこのようにjavascriptの "key"変数を変更しています。
//Fools the website into believing a human is navigating it
((JavascriptExecutor)driver).executeScript("window.key = \"blahblah\";");
多くのサイトはSeleniumによって廃棄されるのを避けるためにこの変数をチェックするので、Google Chromeと一緒にSelenium Webdriverを使うときいくつかのウェブサイトのために働きます。