browser-automation について
CDP、WebDriver、Selenium、Playwright、Puppeteer を使ったブラウザー自動化の実装ガイドです。テスト駆動の手順に加え、ドメイン制限、認証情報の保護、監査ログ、性能改善の方法を扱います。
主な機能
- 01CDP のネットワーク傍受と DOM 操作
- 02WebDriver の要素操作と待機
- 03ドメイン許可リストと URL 検証
- 04認証情報の保護と監査ログ
- 05コンテキスト再利用と並列実行
活用シーン
- 01テスト駆動のブラウザー自動化の計画
- 02自動化対象を許可ドメインに制限
- 03複数 URL 処理の効率改善
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ martinholovsky/ claude-skills-generator --skill browser-automationインストール後、Claude Code で/browser-automationを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/advanced-patterns.mdsecurity-examples.mdthreat-model.md詳細
1. 概要
リスクレベル:高 — Web アクセス、認証情報の取り扱い、データ抽出、ネットワークリクエスト
ブラウザー自動化のエキスパートとして、以下の分野に関する深い専門知識を備えています。
- Chrome DevTools Protocol:Chrome/Chromium の直接制御
- WebDriver/Selenium:クロスブラウザー自動化の標準
- Playwright/Puppeteer:最新の自動化フレームワーク
- セキュリティ制御:ドメイン制限、認証情報の保護
基本原則
- まず TDD — pytest-playwright を使用し、実装前にテストを作成します
- パフォーマンスを意識 — コンテキストを再利用し、並列化し、不要なリソースをブロックします
- セキュリティを最優先 — ドメインの許可リスト、認証情報の保護、監査ログ
- 信頼性の高い自動化 — タイムアウトの適用、適切な待機、エラー処理
主な専門分野
- CDP プロトコル:ネットワークインターセプト、DOM 操作、JavaScript 実行
- WebDriver API:要素の操作、ナビゲーション、待機
- セキュリティ:ドメインの許可リスト、認証情報の取り扱い、監査ログ
- パフォーマンス:リソース管理、並列実行
2. 実装ワークフロー(TDD)
ステップ 1:まず失敗するテストを書く
# tests/test_browser_automation.py
import pytest
from playwright.sync_api import Page, expect
class TestSecureBrowserAutomation:
"""Test secure browser automation with pytest-playwright."""
def test_blocks_banking_domains(self, automation):
"""Test that banking domains are blocked."""
with pytest.raises(SecurityError, match="URL blocked"):
automation.navigate("https://chase.com")
def test_allows_permitted_domains(self, automation):
"""Test navigation to allowed domains."""
automation.navigate("https://example.com")
assert "Example" in automation.page.title()
def test_blocks_password_fields(self, automation):
"""Test that password field filling is blocked."""
automation.navigate("https://example.com/form")
with pytest.raises(SecurityError, match="password"):
automation.fill('input[type="password"]', "secret")
def test_rate_limiting_enforced(self, automation):
"""Test rate limiting prevents abuse."""
for _ in range(60):
automation.check_request()
with pytest.raises(RateLimitError):
automation.check_request()
@pytest.fixture
def automation():
"""Provide configured SecureBrowserAutomation instance."""
auto = SecureBrowserAutomation(
domain_allowlist=['example.com'],
permission_tier='standard'
)
auto.start_session()
yield auto
auto.close()
ステップ 2:テストに合格するための最小限の実装を行う
# Implement just enough to pass tests
class SecureBrowserAutomation:
def navigate(self, url: str):
if not self._validate_url(url):
raise SecurityError(f"URL blocked: {url}")
self.page.goto(url)
ステップ 3:パターンに従ってリファクタリングする
テストに合格したら、以下を追加するためにリファクタリングします。
- 適切なエラー処理
- 監査ログ
- パフォーマンスの最適化
ステップ 4:完全な検証を実行する
# Run all browser automation tests
pytest tests/test_browser_automation.py -v --headed
# Run with coverage
pytest tests/test_browser_automation.py --cov=src/automation --cov-report=term-missing
# Run security-specific tests
pytest tests/test_browser_automation.py -k "security" -v
3. パフォーマンスパターン
パターン 1:ブラウザーコンテキストの再利用
# BAD - Creates new browser for each test
def test_page_one():
browser = playwright.chromium.launch()
page = browser.new_page()
page.goto("https://example.com/one")
browser.close()
def test_page_two():
browser = playwright.chromium.launch() # Slow startup again
page = browser.new_page()
page.goto("https://example.com/two")
browser.close()
# GOOD - Reuse browser context
@pytest.fixture(scope="session")
def browser():
"""Share browser across all tests in session."""
pw = sync_playwright().start()
browser = pw.chromium.launch()
yield browser
browser.close()
pw.stop()
@pytest.fixture
def page(browser):
"""Create fresh context per test for isolation."""
context = browser.new_context()
page = context.new_page()
yield page
context.close()
パターン 2:並列実行
# BAD - Sequential scraping
def scrape_all(urls: list) -> list:
results = []
for url in urls:
page.goto(url)
results.append(page.content())
return results # Very slow for many URLs
# GOOD - Parallel with multiple contexts
def scrape_all_parallel(urls: list, browser, max_workers: int = 4) -> list:
"""Scrape URLs in parallel using multiple contexts."""
from concurrent.futures import ThreadPoolExecutor, as_completed
def scrape_url(url: str) -> str:
context = browser.new_context()
page = context.new_page()
try:
page.goto(url, wait_until='domcontentloaded')
return page.content()
finally:
context.close()
with ThreadPoolExecutor(max_workers=max_workers) as executor:
futures = {executor.submit(scrape_url, url): url for url in urls}
return [future.result() for future in as_completed(futures)]
パターン 3:ネットワークインターセプトによる高速化
# BAD - Load all resources
page.goto("https://example.com") # Loads images, fonts, analytics
# GOOD - Block unnecessary resources
def setup_resource_blocking(page):
"""Block resources that slow down automation."""
page.route("**/*", lambda route: (
route.abort() if route.request.resource_type in [
"image", "media", "font", "stylesheet"
] else route.continue_()
))
# Usage
setup_resource_blocking(page)
page.goto("https://example.com") # 2-3x faster
パターン 4:分析用リクエストのブロック
# BAD - Allow all tracking requests
page.goto(url) # Slow due to analytics loading
# GOOD - Block tracking domains
BLOCKED_DOMAINS = [
'*google-analytics.com*',
'*googletagmanager.com*',
'*facebook.com/tr*',
'*doubleclick.net*',
]
def setup_tracking_blocker(page):
"""Block tracking and analytics requests."""
for pattern in BLOCKED_DOMAINS:
page.route(pattern, lambda route: route.abort())
# Apply before navigation
setup_tracking_blocker(page)
page.goto(url) # Faster, no tracking overhead
パターン 5:効率的なセレクター
# BAD - Slow selectors
page.locator("//div[@class='container']//span[contains(text(), 'Submit')]").click()
page.wait_for_selector(".dynamic-content", timeout=30000)
# GOOD - Fast, specific selectors
page.locator("[data-testid='submit-button']").click() # Direct attribute
page.locator("#unique-id").click() # ID is fastest
# GOOD - Use role selectors for accessibility
page.get_by_role("button", name="Submit").click()
page.get_by_label("Email").fill("[email protected]")
# GOOD - Combine selectors for specificity without XPath
page.locator("form.login >> button[type='submit']").click()
4. 主な責任
4.1 安全な自動化の原則
ブラウザーを自動化する際は、以下を実施します。
- ドメインを許可リストに制限する
- スクリプトに認証情報を保存しない
- 機密性の高い URL(銀行、医療)をブロックする
- すべてのナビゲーションと操作をログに記録する
- すべての操作にタイムアウトを設定する
4.2 セキュリティ最優先のアプローチ
すべてのブラウザー操作で、必ず以下を行います。
- URL をドメインの許可リストと照合して検証する
- 認証情報が露出していないか確認する
- 機密性の高いサイトへのアクセスをブロックする
- 操作の詳細をログに記録する
- タイムアウト制限を適用する
4.3 データの取り扱い
- ページから認証情報を抽出しない
- ログ内の機密データをマスキングする
- セッション終了後にブラウザーの状態を消去する
- 分離されたプロファイルを使用する
5. 技術基盤
5.1 自動化フレームワーク
Chrome DevTools Protocol(CDP):
- ブラウザーの直接制御
- ネットワークインターセプト
- パフォーマンスのプロファイリング
WebDriver/Selenium:
- クロスブラウザー対応
- W3C 標準
最新のフレームワーク:
- Playwright:複数ブラウザー対応、自動待機
- Puppeteer:Chrome 向け CDP ラッパー
5.2 セキュリティに関する考慮事項
| リスク領域 | 緩和策 | 優先度 |
|---|---|---|
| 認証情報の窃取 | ドメインの許可リスト | 重大 |
| フィッシング | URL の検証 | 重大 |
| データの持ち出し | 出力のフィルタリング | 高 |
| セッションハイジャック | 分離されたプロファイル | 高 |
6. 実装パターン
パターン 1:安全なブラウザーセッション
from playwright.sync_api import sync_playwright
import logging
import re
from urllib.parse import urlparse
class SecureBrowserAutomation:
"""Secure browser automation with comprehensive controls."""
BLOCKED_DOMAINS = {
'chase.com', 'bankofamerica.com', 'wellsfargo.com',
'accounts.google.com', 'login.microsoft.com',
'paypal.com', 'venmo.com', 'stripe.com',
}
BLOCKED_URL_PATTERNS = [
r'/login', r'/signin', r'/auth', r'/password',
r'/payment', r'/checkout', r'/billing',
]
def __init__(self, domain_allowlist: list = None, permission_tier: str = 'standard'):
self.domain_allowlist = domain_allowlist
self.permission_tier = permission_tier
self.logger = logging.getLogger('browser.security')
self.timeout = 30000
def start_session(self):
"""Start browser with security settings."""
self.playwright = sync_playwright().start()
self.browser = self.playwright.chromium.launch(
headless=True,
args=['--disable-extensions', '--disable-plugins', '--no-sandbox']
)
self.context = self.browser.new_context(ignore_https_errors=False)
self.context.set_default_timeout(self.timeout)
self.page = self.context.new_page()
def navigate(self, url: str):
"""Navigate with URL validation."""
if not self._validate_url(url):
raise SecurityError(f"URL blocked: {url}")
self._audit_log('navigate', url)
self.page.goto(url, wait_until='networkidle')
def _validate_url(self, url: str) -> bool:
"""Validate URL against security rules."""
parsed = urlparse(url)
domain = parsed.netloc.lower().removeprefix('www.')
if any(domain == d or domain.endswith('.' + d) for d in self.BLOCKED_DOMAINS):
return False
if self.domain_allowlist:
if not any(domain == d or domain.endswith('.' + d) for d in self.domain_allowlist):
return False
return not any(re.search(p, url, re.I) for p in self.BLOCKED_URL_PATTERNS)
def close(self):
"""Clean up browser session."""
if hasattr(self, 'context'):
self.context.clear_cookies()
self.context.close()
if hasattr(self, 'browser'):
self.browser.close()
if hasattr(self, 'playwright'):
self.playwright.stop()
パターン 2:レート制限
import time
class BrowserRateLimiter:
"""Rate limit browser operations."""
def __init__(self, requests_per_minute: int = 60):
self.requests_per_minute = requests_per_minute
self.request_times = []
def check_request(self):
"""Check if request is allowed."""
cutoff = time.time() - 60
self.request_times = [t for t in self.request_times if t > cutoff]
if len(self.request_times) >= self.requests_per_minute:
raise RateLimitError("Request rate limit exceeded")
self.request_times.append(time.time())
7. セキュリティ標準
7.1 重大な脆弱性
| 脆弱性 | CWE | 深刻度 | 緩和策 |
|---|---|---|---|
| 自動化経由の XSS | CWE-79 | 高 | 挿入するスクリプトをサニタイズする |
| 認証情報の収集 | CWE-522 | 重大 | パスワードフィールドへのアクセスをブロックする |
| セッションハイジャック | CWE-384 | 高 | 分離されたプロファイル、セッションの消去 |
| フィッシングの自動化 | CWE-601 | 重大 | ドメインの許可リスト、URL の検証 |
7.2 よくある間違い
# Never: Fill Password Fields
# BAD
page.fill('input[type="password"]', password)
# GOOD
if element.get_attribute('type') == 'password':
raise SecurityError("Cannot fill password fields")
# Never: Access Banking Sites
# BAD
page.goto(user_url)
# GOOD
if not validate_url(user_url):
raise SecurityError("URL blocked")
page.goto(user_url)
8. 実装前チェックリスト
コードを書く前に
- PRD のセクション 8 からセキュリティ要件を読む
- 新しい自動化機能について、失敗するテストを書く
- 対象サイトのドメイン許可リストを定義する
- ブロックまたはマスキングする機密要素を特定する
実装中
- ナビゲーション前に URL 検証を実装する
- すべての操作の監査ログを追加する
- リクエストのインターセプトとブロックを設定する
- すべての操作に適切なタイムアウトを設定する
- パフォーマンス向上のため、ブラウザーコンテキストを再利用する
コミット前
- すべてのテストに合格すること:
pytest tests/test_browser_automation.py - セキュリティテストに合格すること:
pytest -k security - コードやログに認証情報が含まれていないこと
- セッションのクリーンアップを確認すること
- レート制限を設定し、テストすること
9. まとめ
目標は、次のようなブラウザー自動化を作成することです。
- テスト駆動:最初にテストを書き、合格するように実装する
- 高パフォーマンス:コンテキストの再利用、並列化、リソースのブロック
- セキュア:ドメイン制限、認証情報の保護、出力のフィルタリング
- 監査可能:包括的なログ記録、リクエストの追跡
実装の順序:
- 最初に失敗するテストを書く
- テストに合格するための最小限のコードを実装する
- パフォーマンス向上のパターンを取り入れてリファクタリングする
- すべての検証コマンドを実行する
- すべて合格した場合にのみコミットする
参考資料
references/secure-session-full.mdを参照してください — SecureBrowserAutomation クラスの完全な実装references/security-examples.mdを参照してください — その他のセキュリティパターンreferences/threat-model.mdを参照してください — 脅威分析の全容
コメント
Markdown 対応