タイミング攻撃とは、ブラウザ内でのアクションの実行時間を分析することに基づいたボット検出の手法です。Facebook、Google、TikTokなどの最新のアンチフロードシステムは、あなたが何をしているかだけでなく、どれくらいの速さで行っているかも分析します。あまりにも早すぎるクリック、瞬時のページ読み込み、自然な間隔の欠如—これらすべてが自動化を示します。この記事では、Selenium、Puppeteer、およびアンチデテクトブラウザを使用する開発者向けに、タイミング攻撃からの保護のための技術的手法を解説します。
タイミング攻撃とは何か、どのように機能するか
タイミング攻撃は、ユーザーのアクション間の時間間隔を測定することに基づいた自動化検出の手法です。アンチフロードシステムは、ページの読み込みから最初のクリックまでの時間、ユーザーがどれくらい早くスクロールするか、フォームの入力時に間隔があるかどうかを収集します。これらのデータは、実際の人間の行動モデルと比較されます。
保護システムが分析する主な時間メトリックは次のとおりです:
- 最初のインタラクションまでの時間 (TTFI) — ページの読み込みから最初のアクション(クリック、スクロール、テキスト入力)までの時間。ボットは通常、DOMの読み込み後すぐにアクションを開始しますが、人間は0.5〜3秒後に行動します。
- クリックタイミングパターン — クリック間の間隔。自動化されたスクリプトは、通常同じ頻度(例えば、ちょうど2秒ごと)でクリックしますが、人間は無作為に行動します。
- タイピング速度の一貫性 — テキスト入力の速度。ボットは瞬時にテキストを入力するか、文字間に一定の遅延を持ちますが、人間は変動する速度と間隔を持ちます。
- マウスの動きの速度 — カーソルの移動速度。Seleniumはデフォルトでカーソルを瞬時に必要な位置にテレポートさせますが、人間は加速と減速でマウスを動かします。
- スクロール動作 — ページのスクロールパターン。ボットは通常、固定されたピクセル数だけスクロールしますが、人間は不均一に、停止しながらスクロールします。
検出システムは、これらのメトリックを分析するために機械学習を使用します。彼らは行動プロファイルを構築し、ユーザーがボットである可能性を計算します。時間パターンがあまりにも完璧すぎたり、あまりにも早すぎたりすると、それは赤信号です。
重要: タイミング攻撃は、特に大量の自動化に対して効果的です。もしあなたが同じタイミングパターンで100のブラウザを起動している場合、アンチフロードシステムは統計的な異常からそれを簡単に検出します。
タイミングパターンによる自動化検出の手法
現代のアンチフロードシステムは、時間特性の分析に複数の層を使用しています。Facebook、Google、Cloudflareなどのプラットフォームが使用する具体的なテクニックを見ていきましょう。
1. Performance APIの分析
ブラウザは、ページの読み込みに関する詳細なテレメトリを収集するPerformance APIを提供します。アンチフロードシステムは以下を分析します:
// Performance APIのデータ例
performance.timing = {
navigationStart: 1234567890000,
domLoading: 1234567890150, // +150ms
domInteractive: 1234567890300, // +300ms
domComplete: 1234567890500, // +500ms
loadEventEnd: 1234567890600 // +600ms
}
// ボットに対する疑わしいパターン:
// - あまりにも早い読み込み (domComplete < 200ms)
// - イベント間の完璧な間隔
// - 外部リソースの読み込みに遅延がない
ヘッドレスブラウザ(特に古いバージョンのPuppeteerやSelenium)は、通常のブラウザと同じように画像、フォント、その他のリソースを読み込まないため、navigationStartやdomLoadingの値が異常に早く表示されることがよくあります。
2. イベントタイミング分析
JavaScriptトラッカーは、すべてのイベント(クリック、マウスの動き、キー入力)のタイムスタンプを追跡し、パターンを分析します:
// イベントテレメトリ収集の例
const events = [];
document.addEventListener('click', (e) => {
events.push({
type: 'click',
timestamp: performance.now(),
x: e.clientX,
y: e.clientY
});
});
// 疑わしいパターンの分析:
// - クリックがちょうどNミリ秒ごとに発生する
// - クリック前にマウスの微動がない
// - 最初のクリックがページの読み込み直後に発生する
3. キーストロークダイナミクス
フォームを記入する際、アンチフロードシステムはキー入力のダイナミクスを分析します—これは各人間のユニークな生体指標です:
- 保持時間 — キーを押している時間(keydownからkeyupまで)。人間は50〜200msの範囲で変動しますが、ボットは一定です。
- フライトタイム — 一つのキーを離してから次のキーを押すまでの時間。人間は100〜500msの範囲で変動しますが、ボットは固定の遅延を持ちます。
- タイピングリズム — 全体のタイピングリズム。人間は句読点で間を取り、エラーを修正しますが、ボットはそうしません。
検出の例: もしあなたがSeleniumでelement.send_keys("テキスト")を使用している場合、すべてのテキストが1〜2ミリ秒で入力されます—これは自動化を瞬時に示します。
コード内での人間のような遅延の模倣
タイミング攻撃からの保護の第一段階は、アクション間に遅延を追加することです。しかし、単にtime.sleep(2)を挿入するだけではなく、自然な行動を模倣することが重要です。
Python(Selenium)での基本的な遅延の模倣
import time
import random
from selenium import webdriver
from selenium.webdriver.common.by import By
def human_delay(min_sec=0.5, max_sec=2.0):
"""人間を模倣したランダムな遅延"""
delay = random.uniform(min_sec, max_sec)
time.sleep(delay)
driver = webdriver.Chrome()
driver.get("https://example.com")
# 最初のアクション前の遅延(人間はページを読みます)
human_delay(1.5, 4.0)
# 要素をクリック
button = driver.find_element(By.ID, "submit-btn")
button.click()
# 次のアクション前の遅延
human_delay(0.8, 2.5)
正規分布による高度な模倣
一様分布(uniform)は不自然に見えます。人間の遅延は、外れ値を伴う正規分布に従います:
import numpy as np
def realistic_delay(mean=1.5, std_dev=0.5, min_val=0.3, max_val=5.0):
"""
正規分布による遅延
mean: 遅延の平均時間
std_dev: 標準偏差
min_val, max_val: 範囲(極端な値を避けるため)
"""
delay = np.random.normal(mean, std_dev)
delay = max(min_val, min(max_val, delay)) # 範囲を制限
time.sleep(delay)
return delay
# 使用例
realistic_delay(mean=2.0, std_dev=0.7) # 平均2秒、変動あり
コンテキスト遅延
異なるアクションには異なる時間が必要です。さまざまなシナリオに対して遅延プロファイルを作成します:
class HumanBehavior:
"""異なるタイプのアクションのための遅延プロファイル"""
@staticmethod
def page_load_delay():
"""ページ読み込み後の遅延(コンテンツを読む)"""
return realistic_delay(mean=2.5, std_dev=1.0, min_val=1.0, max_val=6.0)
@staticmethod
def before_click():
"""クリック前の遅延(目で要素を探す)"""
return realistic_delay(mean=0.8, std_dev=0.3, min_val=0.3, max_val=2.0)
@staticmethod
def before_typing():
"""テキスト入力を開始する前の遅延"""
return realistic_delay(mean=1.2, std_dev=0.5, min_val=0.5, max_val=3.0)
@staticmethod
def between_form_fields():
"""フォームフィールド間の遅延"""
return realistic_delay(mean=0.6, std_dev=0.2, min_val=0.2, max_val=1.5)
# スクリプトでの使用
driver.get("https://example.com/login")
HumanBehavior.page_load_delay()
username_field = driver.find_element(By.ID, "username")
HumanBehavior.before_typing()
# ... ログイン名を入力 ...
HumanBehavior.between_form_fields()
password_field = driver.find_element(By.ID, "password")
HumanBehavior.before_typing()
# ... パスワードを入力 ...
アクション実行時間のランダム化
アクション間に同じ遅延があることは統計的な異常です。もしあなたがスクリプトの100のインスタンスを実行し、すべてがページの読み込み後ちょうど2.5秒でボタンをクリックしている場合—それは簡単に検出されます。複数のレベルでのランダム化が必要です。
1. アクションの順序のランダム化
アクションの順序に変動を加えます。例えば、フォームを記入する前に時々ページをスクロールし、時々はしない:
def fill_form_naturally(driver):
# フォーム記入前に30%の確率でページをスクロール
if random.random() < 0.3:
driver.execute_script("window.scrollBy(0, 200)")
human_delay(0.5, 1.5)
# 20%の確率でランダムな場所をクリック(読むふりをする)
if random.random() < 0.2:
body = driver.find_element(By.TAG_NAME, "body")
body.click()
human_delay(0.3, 0.8)
# メインアクション — フォームの記入
username_field = driver.find_element(By.ID, "username")
type_like_human(username_field, "myusername")
2. タイピング速度の変動
瞬時にテキストを入力するのではなく、変動する速度で文字ごとに入力を模倣します:
def type_like_human(element, text):
"""人間のタイピング速度を模倣したテキスト入力"""
for char in text:
element.send_keys(char)
# 文字間の基本的な遅延
base_delay = random.uniform(0.05, 0.15)
# スペースや句読点での追加の遅延
if char in [' ', '.', ',', '!', '?']:
base_delay += random.uniform(0.1, 0.3)
# ランダムな「考え中」の時間(5%の確率で長い遅延)
if random.random() < 0.05:
base_delay += random.uniform(0.5, 1.5)
time.sleep(base_delay)
# 時々タイプミスをして修正する(10%の確率)
if random.random() < 0.1:
time.sleep(random.uniform(0.2, 0.5))
element.send_keys(Keys.BACKSPACE)
time.sleep(random.uniform(0.1, 0.3))
element.send_keys(text[-1]) # 最後の文字を再入力
3. Puppeteer: 遅いテキスト入力
Puppeteerには、delayオプションが組み込まれており、type()メソッドに使用できますが、これをランダム化する必要があります:
// 基本的な使用法(推奨されません—固定の遅延)
await page.type('#username', 'myusername', { delay: 100 });
// 正しいアプローチ—各文字のためにランダム化
async function typeWithVariableSpeed(page, selector, text) {
await page.click(selector);
for (const char of text) {
await page.keyboard.type(char);
// 50〜150msのランダムな遅延
let delay = Math.random() * 100 + 50;
// スペースでの追加の遅延
if (char === ' ') {
delay += Math.random() * 200 + 100;
}
// ランダムな長い遅延(5%の確率)
if (Math.random() < 0.05) {
delay += Math.random() * 1000 + 500;
}
await page.waitForTimeout(delay);
}
}
// 使用例
await typeWithVariableSpeed(page, '#username', 'myusername');
自然なマウスの動きとスクロール速度
SeleniumとPuppeteerはデフォルトでマウスカーソルを動かさず、必要な位置に瞬時にテレポートさせてクリックします。これは自動化の最も明白な兆候の一つです。人間のマウスの動きを模倣するには特別なライブラリが必要です。
滑らかなマウスの動きのためのpyautoguiライブラリ
pyautoguiライブラリを使用すると、加速と減速を伴うベジェ曲線に沿ってカーソルを動かすことができます:
import pyautogui
from selenium.webdriver.common.action_chains import ActionChains
def move_mouse_naturally(driver, element):
"""人間を模倣した滑らかなマウスの動き"""
# 要素の座標を取得
location = element.location
size = element.size
# 目標地点(要素の中心 + 少しのランダム性)
target_x = location['x'] + size['width'] / 2 + random.randint(-5, 5)
target_y = location['y'] + size['height'] / 2 + random.randint(-5, 5)
# 変動速度での滑らかな動き
# duration — 動作時間(リアリズムのために0.5〜1.5秒)
duration = random.uniform(0.5, 1.5)
# tweening — 加速関数(easeInOutQuadは人間の動きを模倣します)
pyautogui.moveTo(target_x, target_y, duration=duration, tween=pyautogui.easeInOutQuad)
# クリック前の少しの遅延(人間は瞬時にクリックしません)
time.sleep(random.uniform(0.05, 0.15))
# クリック
element.click()
重要: この方法は、Seleniumが実際のブラウザウィンドウを制御している場合にのみ機能します(ヘッドレスではありません)。ヘッドレスモードでは、マウスの動きは無駄です。なぜなら、アンチフロードシステムはカーソルを見ないからです。
Puppeteer: マウスの動きの模倣
Puppeteerでは、カーソルのリアルな動きを実現するためにghost-cursorライブラリを使用できます:
// インストール: npm install ghost-cursor
const { createCursor } = require("ghost-cursor");
const puppeteer = require("puppeteer");
(async () => {
const browser = await puppeteer.launch({ headless: false });
const page = await browser.newPage();
const cursor = createCursor(page);
await page.goto("https://example.com");
// 要素に滑らかに移動してクリック
const button = await page.$("#submit-btn");
await cursor.click(button); // カーソルがベジェ曲線に沿って動きます!
// 代わりに—座標に移動
await cursor.move("#username"); // クリックせずにカーソルを移動
await page.waitForTimeout(300);
await cursor.click(); // 現在の位置でクリック
})();
スクロールの模倣
人間はページを一度に500ピクセルまっすぐにスクロールしません。スクロールは不均一で、間隔があり、時には後ろに戻ることもあります:
def scroll_like_human(driver, target_position=None):
"""
人間のスクロールを模倣
target_position: ピクセル単位の目標位置(Noneの場合はページの最後までスクロール)
"""
current_position = driver.execute_script("return window.pageYOffset;")
if target_position is None:
# ページの最後までスクロール
target_position = driver.execute_script("return document.body.scrollHeight;")
while current_position < target_position:
# ランダムなスクロールステップ(100〜400ピクセル)
scroll_step = random.randint(100, 400)
current_position += scroll_step
# スクロール
driver.execute_script(f"window.scrollTo(0, {current_position});")
# スクロール間の遅延(人間はコンテンツを読みます)
time.sleep(random.uniform(0.3, 1.2))
# 時々少し戻ってスクロール(10%の確率)
if random.random() < 0.1:
back_scroll = random.randint(50, 150)
current_position -= back_scroll
driver.execute_script(f"window.scrollTo(0, {current_position});")
time.sleep(random.uniform(0.2, 0.6))
# 使用例
scroll_like_human(driver, target_position=2000) # 2000pxまでスクロール
ページとAJAXリクエストの読み込み時間
アンチフロードシステムは、ユーザーのアクションだけでなく、ページの読み込み特性も分析します。ヘッドレスブラウザは、画像を読み込まず、いくつかのスクリプトを実行せず、CSSをレンダリングしないため、異常に早く読み込まれることがよくあります。
問題: あまりにも早い読み込み
通常のブラウザとヘッドレスのPerformance APIの典型的な値を比較します:
| メトリック | 通常のブラウザ | ヘッドレス(疑わしい) |
|---|---|---|
| domContentLoaded | 800-2000 ms | 50-200 ms |
| loadEventEnd | 2000-5000 ms | 100-500 ms |
| 読み込まれたリソースの数 | 50-200(画像、CSS、JS) | 5-20(重要なもののみ) |
解決策: 読み込みの遅延を強制する
ページの読み込み後に人工的な遅延を追加して、メトリックがより自然に見えるようにします:
def load_page_naturally(driver, url):
"""自然な読み込み時間を模倣したページの読み込み"""
start_time = time.time()
driver.get(url)
# DOMの完全な読み込みを待つ
WebDriverWait(driver, 10).until(
lambda d: d.execute_script("return document.readyState") == "complete"
)
# 実際の読み込み時間を計算
actual_load_time = time.time() - start_time
# 読み込みがあまりにも早い場合(< 1秒)、遅延を追加
if actual_load_time < 1.0:
additional_delay = random.uniform(1.0, 2.5) - actual_load_time
time.sleep(additional_delay)
# 「ページを読む」ための追加の遅延
time.sleep(random.uniform(0.5, 2.0))
# 使用例
load_page_naturally(driver, "https://example.com")
AJAXリクエストの待機
現代のウェブサイトは、AJAXを介して非同期にコンテンツを読み込みます。もしあなたのスクリプトがすべてのAJAXリクエストが完了する前にアクションを開始する場合—それは疑わしいです。明示的な待機を使用します:
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
def wait_for_ajax(driver, timeout=10):
"""すべてのAJAXリクエストの完了を待つ(jQuery)"""
WebDriverWait(driver, timeout).until(
lambda d: d.execute_script("return jQuery.active == 0")
)
# jQueryがないサイトの場合—特定の要素を待機
def wait_for_dynamic_content(driver, selector, timeout=10):
"""動的に読み込まれた要素の出現を待つ"""
WebDriverWait(driver, timeout).until(
EC.presence_of_element_located((By.CSS_SELECTOR, selector))
)
# 要素が出現した後の追加の遅延
time.sleep(random.uniform(0.3, 0.8))
アンチデテクトブラウザでの保護設定
アービトラージ、マルチアカウント、または匿名性が重要な他のタスクに取り組んでいる場合は、Dolphin Anty、AdsPower、Multilogin、GoLoginなどのアンチデテクトブラウザを使用してください。これらはタイミング攻撃からの保護メカニズムを内蔵していますが、適切に設定する必要があります。
Dolphin Anty: Human Typingの設定
Dolphin Antyには「Human Typing」機能があり、人間のタイピングを自動的に模倣します。設定方法:
- ブラウザプロファイルを開く → 「Automation」タブ
- 「Human Typing Emulation」を有効にする
- パラメータを設定する:
- 平均タイピング速度: 150-250文字/分(リアルな速度)
- 変動: 30-50%(文字間の速度のばらつき)
- 句読点での遅延: 有効(句読点での遅延)
- ランダムなタイプミス: 2-5%(ランダムなタイプミスと修正)
これにより、DolphinのAPIを介したテキスト入力は自動的に人間を模倣します。
AdsPower: マウスの動きの設定
AdsPowerでは、実際のユーザーのマウスの動きのパターンを記録し、それを再生することができます:
- プロファイルを開く → 「Advanced Settings」 → 「Mouse Behavior」
- 「Record Real User」モードを選択:
- 通常モードでブラウザを開く
- 典型的なアクション(クリック、スクロール、マウスの動き)を実行する
- AdsPowerが動きの軌跡を記録します
- APIを介した自動化時に、AdsPowerは記録されたパターンを変動を持って再生します
Multilogin: Canvas NoiseとWebGL Timing
Multiloginは、CanvasとWebGLのフィンガープリンティングにノイズを追加し、レンダリングに関連するタイミング攻撃に影響を与えます:
- プロファイル → 「Fingerprint settings」 → 「Canvas」
- 「Canvas Noise」を有効にする(Canvasのレンダリングにマイクロ遅延を追加)
- 「WebGL Metadata Masking」を有効にする(レンダリング速度に影響を与えるGPUの特性をマスク)
これにより、Canvas.toDataURL()やWebGL操作の実行時間分析を通じた検出から保護されます。
推奨: Facebook、Instagram、またはTikTokでのマルチアカウント作業を行っている場合は、必ずアンチデテクトブラウザを使用し、質の高いレジデンシャルプロキシと組み合わせてください—これにより、チェーンバンやIPによる検出のリスクが最小限に抑えられます。
タイミング検出を回避するための高度なテクニック
特に保護されたプラットフォーム(Google、Facebook、銀行のウェブサイト)では、基本的な手法では不十分な場合があります。高度なテクニックを見ていきましょう。
1. Performance APIの置換
Performance APIのメソッドをオーバーライドして、実際の値の代わりにリアルな値を返すことができます:
// performance.now()を置換するスクリプトの注入
const script = `
(function() {
const originalNow = performance.now.bind(performance);
let offset = 0;
let lastValue = 0;
performance.now = function() {
const realValue = originalNow();
// 時間にランダムなノイズを追加
const noise = Math.random() * 2 - 1; // -1から+1 msまで
let fakeValue = realValue + offset + noise;
// 単調性を保証する(時間が逆行しない)
if (fakeValue <= lastValue) {
fakeValue = lastValue + 0.1;
}
lastValue = fakeValue;
return fakeValue;
};
})();
`;
// Puppeteer: ページ作成時の注入
await page.evaluateOnNewDocument(script);
注意: この方法は、ネイティブメソッドの整合性チェックを通じて検出される可能性があります。強力な保護がないサイトでのみ使用してください。
2. CPUとネットワークのスロットリング
Chrome DevTools Protocolを使用すると、CPUとネットワークを人工的に遅くして、読み込みメトリックがより自然に見えるようにできます:
// Puppeteer: CPUを2倍に遅くする
const client = await page.target().createCDPSession();
await client.send('Emulation.setCPUThrottlingRate', { rate: 2 });
// ネットワークを遅くする(3Gをエミュレート)
await page.emulateNetworkConditions({
offline: false,
downloadThroughput: 1.5 * 1024 * 1024 / 8, // 1.5 Mbps
uploadThroughput: 750 * 1024 / 8, // 750 Kbps
latency: 40 // 40msの遅延
});
これにより、ページの読み込み時間とJavaScriptの実行時間が増加し、プロファイルが平均的なインターネット速度を持つ実際のユーザーに似たものになります。
3. バックグラウンドアクティビティの模倣
実際のユーザーは一つのタブに留まることはありません—彼らはタブを切り替え、ウィンドウを最小化し、気を散らします。これを模倣します:
async function simulateTabSwitch(page) {
// 別のタブへの切り替えをエミュレート(Page Visibility API)
await page.evaluate(() => {
Object.defineProperty(document, 'hidden', {
get: () => true,
configurable: true
});
document.dispatchEvent(new Event('visibilitychange'));
});
// 遅延(ユーザーは別のタブを見ています)
await page.waitForTimeout(Math.random() * 3000 + 2000);
// タブに戻る
await page.evaluate(() => {
Object.defineProperty(document, 'hidden', {
get: () => false,
configurable: true
});
document.dispatchEvent(new Event('visibilitychange'));
});
}
// 使用例: 作業中にランダムに「気を散らす」
if (Math.random() < 0.15) { // 15%の確率
await simulateTabSwitch(page);
}
4. 実際のUser Timingマークの使用
一部のウェブサイトは、User Timing APIを介して独自のタイムスタンプを作成します。リアルなマークを追加します:
// リアルなタイミングマークの作成
await page.evaluate(() => {
// アクション前にユーザーの「考え」を模倣
performance.mark('user-started-reading');
setTimeout(() => {
performance.mark('user-found-button');
performance.measure('reading-time', 'user-started-reading', 'user-found-button');
}, Math.random() * 2000 + 1000);
});
5. 統計的相関を減らすためのプロキシのローテーション
たとえ各スクリプトのインスタンスがランダム化された遅延を持っていても、すべてのリクエストが同じIPから来ている場合、アンチフロードシステムは相関を検出する可能性があります。プロキシのローテーションを使用します:
- パースや大量のタスクの場合: データセンターのプロキシを使用し、5〜10分ごとに自動的にローテーションします
- ソーシャルメディアや広告に関しては: レジデンシャルプロキシを使用し、セッションにバインドします(スティッキーセッション)
- モバイルプラットフォーム(Instagram、TikTok)用: モバイルプロキシを使用し、タイマーまたはリクエストに応じてローテーションします
IPのローテーションは、アンチフロードシステムがあなたのタイミングパターンの統計分析に必要なデータを収集する可能性を減少させます。
結論
タイミング攻撃は、自動化検出の複雑な手法であり、保護には包括的なアプローチが必要です。主なポイントは次のとおりです:
- 固定の遅延を使用しない—アクション間の時間を常にランダム化し、正規分布を使用します
- コンテキスト遅延を模倣する—異なるアクションには異なる時間が必要です(ページを読むこと ≠ ボタンをクリックすること)
- アクションの順序に変動を加える—ランダムなスクロール、クリック、タブの切り替え
- 滑らかなマウスの動きを使用する—ghost-cursor(Puppeteer)やpyautogui(Selenium)ライブラリを使用します
- 自然なタイピング速度を模倣する—句読点での間隔を持つ文字ごとの入力
- アンチデテクトブラウザを設定する—Dolphin Anty、AdsPower、Multiloginにはタイミング攻撃からの保護メカニズムが組み込まれています
- 質の高いプロキシと組み合わせる—IPのローテーションは統計的分析の可能性を減少させます
理想的な保護は存在しないことを忘れないでください。アンチフロードシステムは常に進化し、新しい検出手法を追加しています。あなたの仕事は、スクリプトの行動を実際のユーザーにできるだけ近づけ、回避手法を定期的に更新することです。
もしあなたがパース、テスト、または他のタスクのためにブラウザの自動化に取り組んでいる場合は、レジデンシャルプロキシを使用することをお勧めします—これにより最大の匿名性が確保され、IPアドレスによる検出のリスクが最小限に抑えられます。モバイルプラットフォームには、実際のモバイルオペレーターのユーザーを模倣するためにモバイルプロキシが最適です。