スマートフォン・タブレットからインターネットサーバーオペレーション

APPW.jp
 
root@appw:~# tail -n 3 /var/log/apache2/access.log
bot と人を分ける ② / 見分ける

UA の外側で、見分ける

本物のブラウザは、細部まで揃っている

appw.jp · server-ops notes · PHP 8.5 / MongoDB

§0入口を通り抜けてくる相手

前回の「止める場所を、手前へ」で、接続と Apache の段階で止める仕組みを整えました。それでも、PHP まで届いた記録の中には、bot が残ります。

本物のブラウザを自動で動かす相手は、形の整ったリクエストを送ってきます。UA も、普通の Chrome そのものです。入口のどの層にも引っかからず、ページまで届きます。

「bot は、3種類いる」で書いたとおり、UA は自己申告にすぎません。では、何を見ればよいのか。答えは2つありました。UA の外側の細部と、ページを開いたあとに届く合図です。

この2つを組み合わせるようになってから、ページの段階で「人」と判定した閲覧と、合図で人と確かめられた閲覧が、8割以上で重なるようになりました。この記事では、その考え方を書きます。

書き方の方針
どのヘッダーの、どんな値を見ているかは書きません。判定の中身を書けば、相手にとっての手直しの手順書になるからです。食い違いの「種類」と、組み合わせ方の考え方を中心にします。

§1ブラウザが自動で作る細部

ブラウザが1回のリクエストで送るのは、UA だけではありません。どの言語を読みたいか、どんな種類の読み込みか、ブラウザの種類と版をもう一度伝えるヘッダー(Client Hints)など、いくつもの情報が一緒に届きます。

これらは全部、同じブラウザのエンジンが、自分の決まりに従って作っています。人が1つずつ選んでいるわけではないので、本物なら、必ず揃います。

道具を作る側は、目に見える UA は本物そっくりに書き写せます。けれども、ほかのヘッダーまで、そのブラウザの決まりどおりに揃えるのは手間がかかります。どこか1か所で、別のブラウザの癖が混ざります。

食い違いの種類

食い違いたとえるなら
名乗った版と、別のヘッダーが示す版が違う名刺と社員証で、会社名が違う
その版なら必ず送るはずのものがない制服を着ているのに、名札を付けていない
別のブラウザの書き方の癖が混ざる標準語で話しているのに、ところどころ別の地方の言い回しが出る
今のブラウザがもう書かない書き方をしているとうに変わった古い住所の書き方のまま

広く知られた例を1つだけ挙げます。Safari を名乗っているのに、Chromium の版を伝える Sec-CH-UA を送ってくる相手です。Safari はこのヘッダーを送りません。送ってくるのは、中身が Chromium だからです。UA だけを iPhone の Safari に差し替えた、自動操作のブラウザによく見られます。

裏の名札に、自分で書いていた

ある道具は、毎回2段階でトップページを取りに来ていました。1回目は HTML だけの単純な取得で、1分ほどあとの2回目は、本物のブラウザで描画しています。表の UA は iPhone の Safari でしたが、2回目の裏の名札(Client Hints)には、「headless の Chrome」とそのまま書いてありました。

手間をかけた道具ほど、どこかで本当の中身が見えます。表を本物に似せるほど、裏との食い違いが目立つのです。

§2ビーコン:人である証拠

細部の食い違いは「bot らしさ」の証拠です。逆に、「人らしさ」の証拠も欲しくなります。それがビーコンです。

ページに小さな JS を置き、読み手がしばらくページを見ていたときか、画面に触れたときに、1回だけ合図を送ります(navigator.sendBeacon)。合図は記録の1件ごとに結びつけてあり、届いたら、その記録に印を付けます。

人のブラウザは JS を動かすので、合図が来ます。HTML だけを取りに来る道具からは、来ません。

判定と合図の、4つの組み合わせ

ページの段階の判定(bot か、人か)と、合図の有無を組み合わせると、記録は4つに分かれます。

人と判定 × 合図あり
確かめられた人
数字の土台にするのは、ここ。
人と判定 × 合図なし
確かめられない
すぐ閉じた人か、すり抜けた道具か。決めずに置いておく。
bot と判定 × 合図あり
JS を動かす bot か、誤判定
いちばん注意して見る場所。
bot と判定 × 合図なし
ふつうの bot
判定と合図が、どちらも bot と言っている。

ログ一覧では、この組み合わせをバッジで並べて表示しています。1件ずつ眺めるだけで、どこに迷いがあるかが分かります。

合図があっても、人とは限らない

本物のブラウザを裏で動かす道具は、JS も動かします。ですから、合図は来ます。§1 の2段階の道具も、2回目には合図が届いていました。

ただ、そうした合図には、画面に触れた跡がありません。時間がたったことで送られた合図だけが並びます。とはいえ、読むだけで画面に触れない人もいるので、触れた跡がないことだけを理由に bot とは決められません。

だから、合図は単独では使いません。「細部が揃っていて、合図も来た」ときに、はじめて人として数えます。bot らしさの証拠と、人らしさの証拠を、両方から見るのが基本です。

§3どちらでもないもの

ログを1件ずつ見ていくと、bot とも人とも言い切れない記録が出てきます。無理にどちらかへ入れると、数字がゆがみます。

先読み

Chrome は、検索結果の上位のページを、読み手が押す前に先回りして取りに来ることがあります。Google 検索からの先読みは、Google の中継を通って届きます。このとき、リクエストには Sec-Purpose: prefetch が付いています。

読み手がそのあと結果を押すと、ブラウザは先に取っておいたものを表示します。サーバーには2回目のリクエストが来ません。記録は先読みの1件だけで、そこに合図が届けば「開かれた」、届かなければ「空振り」です。

  • 先読みの記録の IP は、読み手ではなく中継のものです。IP をもとにした判定は、当てはめないように気をつけます。
  • アドレスバーに文字を打ち始めたときに、裏で描画まで済ませておく先読みもあります。こちらは、一度来たことのある人の再訪のしるしになります。
  • ログ一覧には、先読みの印(PF・PR)を出すようにしました。

アプリの中の、一瞬の表示

UA の末尾に Safari の名前がない iPhone や iPad からの記録が、合図なしで並んでいたことがありました。アプリが自分の中にページを表示する部品を使ったときの UA です。

Apache のログを並べると、HTML のあとに CSS も JS も読み込まれていて、JS が別の通信を始めた跡までありました。それなのに、合図の送り先にだけ来ていません。つまり、合図を送る前に閉じられていました。最初のリクエストは、昔の http:// のリンクからの転送でした。どこかに保存された古いリンクの、見た目づくりのための読み込みでしょう。

これは bot ではありません。けれども、人が読んだとも言えません。「確かめられない」のままにしておくのが正しい扱いです。合図の条件を緩めれば数字は増えますが、それは見た目の数字です。

迷ったら Apache のログ
HTML しか取りに来ない → 道具。CSS・JS まで読むのに合図が来ない → 一瞬の表示。合図の送り先まで届く → 人の閲覧。この3つは、Apache のログを並べるだけで見分けられます。

古いブラウザのまま使う人

古い版のブラウザは、bot がよく名乗る形でもあるので、ページの段階では bot と判定します。ところが、その中に人がいました。会社が管理するクラウドのブラウザ環境から、版が止まった Chrome で、表計算のページを繰り返し見ている人です。

そこで、いったん bot と判定した記録でも、人の証拠が揃えば、あとから人に戻すようにしました(救済)。

ここで、もう1つ問題が見つかりました。bot と判定した記録には、毎回新しいセッションの印を付けていたのです。そのため、救済された人は、1ページごとに別の人として数えられていました。2回の訪問が、8つのセッションになっていました。戻す仕組みを作ったら、数え方も一緒に直す必要がありました。

§4判定を重ねる

1つ直されても、ほかが残る

道具の作者は、捕まると原因を1つずつ直してきます。UA を変え、足りないヘッダーを足し、版を揃える。1つの食い違いだけで捕まえていると、それを直された時点ですり抜けます。

いくつもの独立した食い違いで、同時に捕まえるようにしておけば、1つ直されても、ほかが残ります。そのために、判定の理由は1つ目だけでなく、当てはまったものを全部、記録に残しています。

守りの薄いところを数える

理由を全部残しておくと、「理由が1つだけで捕まっている記録」を数えられます。そこが、守りの薄いところです。

とくに、合図が届いているのに、理由が1つだけの bot。本物のブラウザを動かす相手が、たった1つの食い違いで捕まっている状態です。その1つを直されれば、そのまま「確かめられた人」の側へ移ってしまいます。数字にいちばん直接響くので、定期的に数えて、別の角度からも捕まえられるように備えます。

大まかな方針は、ヘッダーの整合性で大半をふるい落とし、残りは振る舞いで確かめる、という二段構えです。ヘッダーがすべて本物の相手でも、どう渡り歩くかには、人とは違う形が出ます。

足す前に、人を数える

新しい判定を思いついたら、足す前に、その形に当てはまる過去の記録のうち、合図で人と確かめられたものが何件あるかを数えます。0件のときだけ足します。

1件でもあれば、その人の記録を見に行きます。古い版のまま使う人や、会社の管理する環境から使う人がいることがあります。実際、ある候補は「版で線を引く」形でしたが、少し古い版に人が何人かいたので、取りやめました。

足した判定と見送った判定は、日付、きっかけ、確かめた結果と一緒に、手元の台帳に1行ずつ残しています。公開しない台帳です。

§5デモ:記録を仕分ける

例の記録を8枚並べました。それぞれを「人」「bot」「決めない」のどれかに仕分けてください。押すと答えと理由が出ます。項目は、実際のヘッダーではなく、この記事の言葉に置き換えてあります。

// record sorter

§6まとめ

  • UA は自己申告。見るのは、UA の外側の細部が、そのブラウザの決まりどおりに揃っているかどうか。
  • 合図は、人らしさの証拠。ただし単独では使わず、細部の整合と組み合わせる。
  • 二択で割り切らない。先読み、一瞬の表示、古いブラウザの人。「決めない」と「あとで戻す」を用意しておく。
  • 判定は重ね、薄いところを数え、足す前に人を数える。

こうして、ページの段階で「人」と判定した数字を、信頼できるようになりました。数字を信頼できると、bot を除くことそのものより、その先に見えるもののほうが面白くなります。

次回は、その数字から見えてきたものを書きます。1件では分からない動きを、あとからどう拾うか。そして、人がページをどう使っているか、です。

// series:bot と人を分ける
  1. 止める場所を、手前へ
    ipset・.htaccess・410
  2. UA の外側で、見分ける(この記事)
    細部の整合・ビーコン・どちらでもないもの
  3. 数字を信頼できて、見えたもの
    見直し・確かめ・見張りと、人の動き

関連記事

『UA の外側で、見分ける — 本物のブラウザは、細部まで揃っている』を公開しました。