Bot対策機能仕様書 (Cloudflare Turnstile導入 & レートリミット)
1. 概要
download.php からの直接ダウンロードに対して、Bot判定ロジックとCloudflare Turnstileを用いた認証を導入する。
正規ユーザーの利便性を損なわないよう、怪しい挙動(大量ダウンロード等)が検知された場合のみ認証を要求する(Adaptive Challenge)。
2. 目的
- スクリプトやBotによる機械的な連続ダウンロード(24時間で1000件以上など)の遮断。
- サーバーリソースの保護。
3. 実装詳細
3.1 処理フロー (src/download.php)
セッション初期化と確認
init_session()後、session_status()を確認し、必要であればsession_start()を実行。$_SESSION['turnstile_verified_until']を確認。- 現在時刻と比較し、有効期限内(1時間以内)であれば Bot判定をスキップ してダウンロード処理へ移行。
Bot判定ロジック (Detection)
- セッション認証がない場合、以下の順で判定を行う。
- A. User-Agent判定
getServer('HTTP_USER_AGENT')を取得。config.phpのBOT_BLOCK_UA_LIST配列をループし、部分一致 (stripos) でチェック。- 一致した場合 → **Bot認定 (
$is_bot_behavior = true)**。
- B. レートリミット判定 (Analytics Log)
config.phpのUSE_ANALYTICS_LOGがtrueの場合:DBA_analyticsクラスを使用し、IPアドレスを元にしたダウンロード回数をチェックする。- 短期間チェック:
getDownloadCountByIp($ip, 600)>BOT_LIMIT_SHORT_TERM - 長期間チェック:
getDownloadCountByIp($ip, 86400)>BOT_LIMIT_LONG_TERM - いずれかの閾値を超過した場合 → **Bot認定 (
$is_bot_behavior = true)**。
config.phpのUSE_ANALYTICS_LOGがfalseの場合:- レートリミットによる判定が実行できないため、安全策として Bot認定 (
$is_bot_behavior = true) とみなし、Turnstile認証を要求する。
- レートリミットによる判定が実行できないため、安全策として Bot認定 (
認証プロセス (Challenge & Verification)
- Bot認定 された場合、以下の処理を行う。
- POSTリクエスト受信時 (検証):
$_POST['cf-turnstile-response']の存在を確認。- Cloudflare API (
https://challenges.cloudflare.com/turnstile/v0/siteverify) にsecret,response,remoteipをPOST送信。 - 検証成功 (
success: true):$_SESSION['turnstile_verified_until']にtime() + 3600を設定。header("Location: ...")で現在のURL(クエリパラメータ含む)へリダイレクト。
- 検証失敗:
- 認証画面表示へ進む。
- 画面表示:
- Smartyテンプレート
turnstile.tplを表示。 turnstile_site_key,action_url,turnstile_action('download') をアサイン。- ダウンロード処理を **中断 (
return)**。
- Smartyテンプレート
3.2 データベース設計 (src/phplib/dba/DBA_analytics.php)
- テーブル:
analytics_log(既存) - インデックス追加:
CREATE INDEX IF NOT EXISTS client_ip_idx ON analytics_log (client_ip, timestamp)- コンストラクタ (
__construct) 内で実行し、IPアドレスによる期間検索を高速化する。
- メソッド追加:
getDownloadCountByIp($ip, $seconds)- SQL:
SELECT count(*) FROM analytics_log WHERE event_source = 'download' AND client_ip = :ip AND timestamp > :threshold
- SQL:
3.3 設定項目 (src/config.php)
以下の定数を追加する。
| 定数名 | 説明 | デフォルト値 |
|---|---|---|
TURNSTILE_SITE_KEY |
Cloudflare Turnstile Site Key | (空文字) |
TURNSTILE_SECRET_KEY |
Cloudflare Turnstile Secret Key | (空文字) |
BOT_BLOCK_UA_LIST |
ブロック対象User-Agentリスト | ['curl', 'wget', 'python', 'bot', 'crawler', 'spider'] |
BOT_LIMIT_SHORT_TERM |
短期間(10分)の許容DL数 | 100 |
BOT_LIMIT_LONG_TERM |
長期間(24時間)の許容DL数 | 1000 |
3.4 画面仕様 (src/templates/turnstile.tpl)
- ウィジェットモード:
- Managed (マネージド) を採用する。
- 理由: Bot疑いのあるアクセスに対してのみ表示する仕様のため、ユーザーに明示的なアクション(チェックボックスのクリック)を求め、人間であることを確認させるフローが適切であるため。
- HTML構造:
- Cloudflare提供のJS:
<script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script> - フォーム:
<form action="{$action_url}" method="POST"> - ウィジェット:
<div class="cf-turnstile" data-sitekey="{$turnstile_site_key}" data-action="{$turnstile_action|escape}"></div>
- Cloudflare提供のJS:
- デザイン:
- シンプルで軽量なデザイン。
- ユーザーに「なぜ確認が必要か」を説明するメッセージを表示。
3.5 Cloudflare設定 (Dashboard)
Turnstileのウィジェット作成時に以下の設定を行うこと。
Widget Mode: Managed (マネージド) を選択する。
Pre-Clearance: 有効 (On) に設定する。
- これにより、チャレンジ通過時にCloudflareがクリアランスCookieを発行し、同一ドメイン内の他の保護リソースへのアクセスをスムーズにする。
Bot Fight Mode:
- Security > Bots メニューから Bot Fight Mode を有効にすることを推奨。
- これにより、既知のBotからのアクセスに対して、Turnstile以前の段階でJavaScriptチャレンジ等が実行され、サーバー負荷を軽減できる。
4. 補足事項
- Cookie依存: セッションを利用するため、Cookieが無効なクライアント(多くのスクリプトBot)は、Bot判定に引っかかる限り毎回認証画面が表示され、ダウンロードできない。
- ログ保存: Bot判定でブロックされたアクセス自体は
analytics_logには記録されない(ダウンロード実行前にブロックするため)。ただし、認証画面の表示回数などを記録する拡張も検討可能。 - 認証リトライの許容: 現在の実装では、認証に失敗した場合でも再度チャレンジ画面が表示され、ユーザーは再試行が可能になっている。これは、Cloudflare Turnstile自体が、同一ユーザーからの連続した失敗など不審な振る舞いを検知し、自動的にチャレンジの難易度を上げたり、最終的にトークンの発行を拒否したりするインテリジェンスを持っているため、ある程度許容される設計である。オリジンサーバー側での無限リトライを防ぐための主要な防御は、Turnstileサービス側で機能する。