アクセスログを見ていると稀にユーザーエージェントに Chrome/98
や Firefox/48
というようなものを見かけることがあります。ご丁寧にユーザーエージェントを改ざんしているけどどう考えてもおかしいものです(Firefox 48 なんて 2016 年リリース)。
Cloudflare WAF ではこれらのブラウザのバージョンを細かく指定することができません(Cloudflare に限らず難しいとは思いますが)。出来ることとしては部分一致で指定する方法です。以下のように指定することで Google Chrome の 90 番台のバージョンを指定することができます。2024 年 8 月現在の Google Chrome の最新バージョンは 127.0.0.0
で、バージョン 900 番台に到達するのは恐らく数十年後でしょうし、それまで User-Agent が使われているかどうかもわかりませんので問題は無いでしょう。
lower(http.user_agent) contains "chrome/9"
同じように chrome/8
、chrome/7
と追加していたんですが最近 Cloudflare WAF の容量問題(4 KB まで)でルールを少し整理する必要が出てきたのでいくつかのルールを Web サーバーの方に移すことにしました。
Apache にはいくつか数値比較が出来る手段が用意されていますが、今回は mod_rewrite を使うことにします(他には <If>
や Require expr
を使う方法があります)。うちのブログは万人に見てもらいたいわけではないので Chrome と Firefox の 100 未満を使っているものはアクセス拒否してしまいます。User-Agent が空のものは Cloudflare で len(http.user_agent) eq 0
で判定してブロック可能ですがこちらにも BrowserMatch ^$
(または BrowserMatchNoCase ^$
)として条件に入れてあります。
# 初期値設定 BrowserMatch . APPLEWEBKIT_VERSION=999 BrowserMatch . CHROME_VERSION=999 BrowserMatch . EDGE_VERSION=999 BrowserMatch . FIREFOX_VERSION=999 BrowserMatch . MOZILLA_VERSION=999 BrowserMatch . SAFARI_VERSION=999 BrowserMatch . DENY_VERSION=999 # バージョン取得 BrowserMatchNoCase AppleWebKit/(\d+) APPLEWEBKIT_VERSION=$1 BrowserMatchNoCase Chrome/(\d+) CHROME_VERSION=$1 BrowserMatchNoCase CriOS/(\d+) CHROME_VERSION=$1 BrowserMatchNoCase Edg(A|e|iOS)?/(\d+) EDGE_VERSION=$1 BrowserMatchNoCase Firefox/(\d+) FIREFOX_VERSION=$1 BrowserMatchNoCase Mozilla/(\d+) MOZILLA_VERSION=$1 BrowserMatchNoCase Safari/(\d+) SAFARI_VERSION=$1 BrowserMatchNoCase ^$ !DENY_VERSION BrowserMatchNoCase AhrefsBot !DENY_VERSION BrowserMatchNoCase GPTBot !DENY_VERSION BrowserMatchNoCase MJ12bot !DENY_VERSION BrowserMatchNoCase MSIE !DENY_VERSION BrowserMatchNoCase SemrushBot !DENY_VERSION BrowserMatchNoCase Trident !DENY_VERSION # 正規のボットを除外(要 Cloudflare カスタムヘッダー) RewriteCond %{HTTP_X_CLIENT_BOT} !=true # バージョン指定 RewriteCond %{ENV:APPLEWEBKIT_VERSION} -lt537 [OR] RewriteCond %{ENV:CHROME_VERSION} -lt100 [OR] RewriteCond %{ENV:EDGE_VERSION} -lt100 [OR] RewriteCond %{ENV:FIREFOX_VERSION} -lt100 [OR] RewriteCond %{ENV:MOZILLA_VERSION} -lt5 [OR] RewriteCond %{ENV:SAFARI_VERSION} -lt537 [OR] RewriteCond %{ENV:DENY_VERSION} -lt999 # アクセス拒否 RewriteRule ^ - [F]
まず、BrowserMatch
(または SetEnvIf User-Agent
)で各ブラウザの初期値を設定します。
これは RewriteCond
の数値比較では定義されていない変数の値が 0
として計算されるためです。なお、SetEnv
による変数の定義は後から上書き出来ないようです。
BrowserMatch . APPLEWEBKIT_VERSION=999
次に User-Agent の値からメージャーバージョンを取得します。例えば User-Agent に AppleWebKit/537.36
が含まれる場合、APPLEWEBKIT_VERSION
の値は 999
から 537
に変更されます。出来ればマイナーバージョンまで比較したいところですが Apache では整数でしか比較できないようです。
BrowserMatchNoCase AppleWebKit/(\d+) APPLEWEBKIT_VERSION=$1
最後に RewriteCond
の数値比較で指定した値よりも古いバージョンを拒否するようにします。
RewriteCond %{ENV:APPLEWEBKIT_VERSION} -lt537 RewriteRule ^ - [F]
ここで注意しなければならないのが善良なクローラーやボットが未だに古いバージョンの User-Agent を指定している場合です。Cloudflare で cf.client.bot
または cf.bot_management.verified_bot
を任意のヘッダー(例:X-CLIENT-BOT
)としてヘッダーに追加している場合はこの値が true
として送られてくるのでこれを使えばいいのですが、Cloudflare を使っていない場合はボットの User-Agent で指定する必要があるでしょう。User-Agent のエスケープに関しては PHP の preg_quote()
関数などを使うのが楽です。
※この User-Agent に偽装された場合でも適切に正規のボットを識別できるよう Cloudflare の cf.client.bot
または cf.bot_management.verified_bot
の使用を推奨します。
RewriteCond %{HTTP_USER_AGENT} "!^Mozilla/5\.0 AppleWebKit/537\.36 \(KHTML, like Gecko; compatible; Googlebot/2\.1; \+http\://www\.google\.com/bot\.html\) Chrome/\d+\.\d+\.\d+\.\d+ Safari/537\.36$" RewriteCond %{HTTP_USER_AGENT} "!^Mozilla/5\.0 \(Linux; Android 6\.0\.1; Nexus 5X Build/MMB29P\) AppleWebKit/537\.36 \(KHTML, like Gecko\) Chrome/\d+\.\d+\.\d+\.\d+ Mobile Safari/537\.36 \(compatible; Googlebot/2\.1; \+http\://www\.google\.com/bot\.html\)$" RewriteCond %{HTTP_USER_AGENT} "!^Mozilla/5\.0 \(compatible; Googlebot/2\.1; \+http\://www\.google\.com/bot\.html\)$" RewriteCond %{ENV:APPLEWEBKIT_VERSION} -lt537 RewriteRule ^ - [F]
バージョンに関係なくブロックしたい User-Agent は変数を削除することで値が 0
となるため、下記の数値比較は 0 < 999
となりアクセスは拒否されます。なお、これに関してはもっと楽な記述で拒否できます。
BrowserMatch . DENY_VERSION=999 BrowserMatchNoCase MJ12bot !DENY_VERSION RewriteCond %{ENV:DENY_VERSION} -lt999 RewriteRule ^ - [F] # 単純な拒否に関してはこっちの方が簡潔 RewriteCond %{HTTP_USER_AGENT} MJ12bot [NC] RewriteRule ^ - [F]
比較演算子に <
や <=
もありますがこれらは文字列での比較になるため誤った結果になる可能性があります。例えば下記は数値的に見れば同じ値ですが右辺の方が大きい結果になります。
RewriteCond 100.0 <100.00
Edge に関しては User-Agent が酷い仕様で Edge
かと思いきや実は Edg
。他にも Edge
、EdgA
、EdgiOS
があるようです。Windows 10 Mobile とか Xbox の場合は Chrome のバージョンと一致しないようなんですが面倒くさいからいいや…。
https://www.whatismybrowser.com/guides/the-latest-user-agent/edge
迷惑なアクセスは本当に滅んで欲しいですね。