mattintosh note

どこかのエンジニアモドキの備忘録

2024-06-05: 現在ホビー関連の記事を 新しいブログ に移行しています。

Cloudflareの調整と画像の外部呼び出し禁止設定

Cloudflare のイベントログを見ていて怪しいなと思ったものを片っ端からブロックしていきます。

ASN によるブロック

マネージドブロックに引っかかっているものを見つけたら IP アドレスや AS 番号を控えます。

IP アドレスを ipinfo.io で調べて Hosting となっていればホスティングサービス(クラウドレンタルサーバ等)なのでブロックします。

例えば今攻撃を仕掛けている 23.91.97.11 というアドレスは ipinfo.io で調べるとこんな感じになっています。

ipinfo.io

ipinfo.io
ipinfo.io

UCLOUD というクラウド系でホスティングサービスに該当します。UA が入っていてそれだけでは一見普通のアクセスに見えますが昨今のブラウザでは HTTP/2.0 に対応していますし、そもそもリクエストパスが有りえないものになっています。なので通常のアクセスではないと判断できます。

{
  "action": "block",
  "clientASNDescription": "UCLOUD-HK-AS-AP UCLOUD INFORMATION TECHNOLOGY HK LIMITED",
  "clientAsn": "135377",
  "clientCountryName": "US",
  "clientIP": "23.91.97.11",
  "clientRequestHTTPHost": "hobby.mattintosh-note.jp",
  "clientRequestHTTPMethodName": "GET",
  "clientRequestHTTPProtocol": "HTTP/1.1",
  "clientRequestPath": "/static/home/css/feiqi-ee5401a8e6.css",
  "clientRequestQuery": "",
  "datetime": "2024-06-23T14:12:25Z",
  "rayName": "898512aa7fd96e55",
  "ref": "",
  "ruleId": "asn",
  "rulesetId": "",
  "source": "asn",
  "userAgent": "Mozilla/5.0 (Linux; Android 11; vivo 1906; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/87.0.4280.141 Mobile Safari/537.36 VivoBrowser/8.9.0.0 uni-app Html5Plus/1.0",
  "matchIndex": 0,
  "metadata": [],
  "sampleInterval": 4
}

Cloudflare
Cloudflare

ipinfo.io では IP アドレスのレンジを調べることが出来、今回は 23.91.97.0/24 となっていますが、この UCLOUD が持っている IP はそれ以上にあります。なので Cloudflare では AS 番号(ASN)を使ってブロックします。

Cloudflare の WAF では「カスタム ルール」とツールにある「IP アクセス ルール」で ASN を使用した制限をかけることができます。どちらを使っても構わないのですが順番があり、「IP アクセス ルール」が先に適用されます。

私の場合は完全にブロックする ASN を「IP アクセス ルール」、マネージドレスポンスにするものを「カスタム ルールにしています」(今のところ)。これは例えば Google の ASN を「IP アクセス ルール」でブロックしてしまうとボット系の条件を設定する「カスタム ルール」が後になってしまい、すべて「IP アクセス ルール」でブロックされてしまうからです。

余談ですが「IP アクセス ルール」はフリープランだと国別の制限がかけられないようなので「カスタム ルール」を使用する必要があります。従って国別の制限をかける場合は 5 つまで設定可能な「カスタム ルール」をひとつ消費することになります。

「IP アクセス ルール」にルールを設定していきます。「IP アクセス ルール」では「カスタム ルール」のように細かい条件は指定出来ず、IP、IP 範囲(/16/24 しか設定できないみたいです)、国名(フリープランは設定不可)または ASN でのみ設定が可能です。

特に弾いても問題ないかなと思われる ASN を登録してみました。FACEBOOK も入ってますが別に facebookexternalhit の恩恵を受けようとは思わないのでブロックしてしまいます。有名な SEMRUSH もブロックしてます。

Cloudflare
Cloudflare

この後「カスタム ルール」の方でボットをマネージドチャレンジで防いでいくのですが、これだけでは例えば AWS EC2 などからのアクセスはブロックできない(稀にボットとして識別されることはある)ので「カスタム ルール」の方で ASN を使って制限をかけていきます。

Cloudflare
Cloudflare

これでボットのチェックから外れた AWSGoogle Cloud Platform 等の IP に対してマネージドチェレンジをしかけることができます。

順番に並べると下記のような状態です。

  1. 「IP アクセス ルール」で ASN でブロック
  2. 「カスタム ルール」で国別でブロック
  3. 「カスタムルール」でボットにスキップまたはマネージドチャレンジ
  4. 「カスタム ルール」で ASN でマネージドチャレンジまたはブロック

画像の外部呼び出し禁止設定

外部のサイトに URL を貼られて呼び出されるとこちらのサーバーの負担になるのでブログ外からの呼び出しをブロックするようにします。

式ビルダーと .htaccess を使用する場合

Cloudflare の「カスタム ルール」ではリファラも条件に指定できるのですがリファラ選択時のオペレーターに「次から始まる」がありません。式ビルダーを使用しなければ指定は可能ですが、式ビルダーで対応したい場合は「次を含む」を使うことになります。これでは条件がややアバウトになってしまいますので正確に指定したいためサーバー側の .htaccess で設定します。

まず先に Cloudflare のキャッシュを無効にしておきます。これをしないとリクエストがサーバーに到達しないため .htaccess で制限をかけても意味がありません。

Cloudflare の Cache Rules で条件を指定します。ここでは WordPress でユーザーがアップロードした画像(/wp-content/uploads 以下)のキャッシュを無効にします。

Cloudflare
Cloudflare

次に .htaccessリファラがサイトのドメインと一致しない場合にブロックするようにします。WordPress や一部の優良なボットはリファラ無しでアクセスしてくることがあるのでこの条件から除外するようにしておきます。

RewriteCond %{HTTP_REFERER} !^https?://example\.com/ [NC]
RewriteCond %{HTTP_USER_AGENT} !^WordPress/\d\.\d\.\d\.$ [NC]
RewriteCond %{HTTP_USER_AGENT} !=Hoge/1.0 [NC]
RewriteRule \.(?i:bmp|gif|jpe?g|png|svg|webp) - [F]

あとはブログ上での表示の確認と URL 直打ちの状態を確認して直打ちで 403 になれば OK です。

こちらは使用していないので説明を読んだだけです。

Scrape Sheiled > Hotlink の保護で直リンクのブロックができるようになるようです。ただし、

Hotlink protection has no impact on crawling, but it will prevent the images from being displayed on sites such as Google images, Pinterest, and Facebook.

という説明があるため Google などからの画像の呼び出しに影響があるようです。技術的にはリファラを見ているようなので後述の WAF で同じようなことができるはずです。

構成ルールと組み合わせることで柔軟に対応できそうです。

式を自前で書く(少し難易度が高いがおすすめ)

先程は Cloudflare のキャッシュを無効にする必要がありましたがこちらは無効にする必要はありません。

Cloudflare のフィールドと関数のドキュメントを参考にしていきます。なお、ワイルドカードについては上位のプランでないと使用できないみたい?

まず WordPress のユーザーがアップロードした画像の外部呼び出しに該当する条件として、

  • URI/wp-content/uploads/ で始まる
  • リファラが自サイト(ここでは例として https://example.com とします)ではない

の式を作ります。URIhttp.request.urihttp.request.uri.path で取得出来ますが後者はクエリが付きません。http.request.uri.path.extension もありますので拡張子指定でサイト全体の画像の呼び出しを禁止することもできるでしょう。サイトは HTTPS 化していることを前提としています。

starts_with(http.request.uri.path, "/wp-content/uploads/")
not starts_with(http.referer, "https://example.com/")

サイトのアイコンに設定している画像へのアクセスはこれらの条件から除外します。メディアにアップロードされたファイルは XXX.png 以外に XXX-32x32.png などのバリエーションが作成されるため先頭一致で指定させます。

not starts_with(http.request.uri.path, "/wp-content/uploads/2024/06/XXX")

Google の画像用ボットなどはリファラ無しでアクセスしてくることがあるのでこれらも許可するようにしておきます。これは in を使った方が対象が増えたときの対応が楽だと思います。もしくは既知のボットのカテゴリでサーチエンジンを指定するのもいいかもしれません(この場合は Bingbot なども許可されます)。

not http.user_agent in {"Googlebot-Image/1.0" "Hoge/1.0"}
または
not cf.verified_bot_category in {"Search Engine Crawler"}

Google 画像検索の場合、サムネイルをクリックすると場合によってはサーバーにリクエストが発生しているようです。リクエストをブロックしても画像が表示されないことはありませんが低解像度になったりするのかな?なのでリファラGoogle も入れておくと良いかもしれません。この指定は少しアバウトなので厳密に制限したい場合は各国の Google 検索のドメインを調べて指定してください。日本限定であれば https://www.google.com/https://www.google.co.jp/ を指定しておけばよいでしょう。

not starts_with(http.referer, "https://www.google.")
日本限定なら
not starts_with(http.referer, "https://www.google.com/") and
not starts_with(http.referer, "https://www.google.co.jp/")

WordPress は自サーバーから画像にアクセスすることがあるのでサーバーの IP アドレスからのアクセスを許可する条件も用意します。ここでも同じく in を使います。サーバーの IP アドレスは DNSアクセスログ等で調べてください。

not ip.src in {192.168.0.1}

あとはこれらの条件を and で結合します。Cloudflare の式は今のところコメントを書くことができませんがここでは # で説明を記載しておきます。

# WordPress のユーザーアップロードディレクトリへのアクセス
starts_with(http.request.uri.path, "/wp-content/uploads/")
# サイトアイコンへのアクセスは許可
and not starts_with(http.request.uri.path, "/wp-content/uploads/2024/06/XXX")
# リファラが自サイトの場合は許可
and not starts_with(http.referer, "https://example.com/")
# リファラが Google であれば許可
and not starts_with(http.referer, "https://www.google.")
# ユーザーエージェントが下記に含まれるなら許可
and not http.user_agent in {"Googlebot-Image/1.0" "Hoge/1.0"}
# 自サーバーからのアクセスは許可
and not ip.src in {192.168.0.1}

これで /wp-content/uploads/ 以下のアクセスに対して、リファラが正しくなく、特定のユーザーエージェントでもなく、特定の IP ではない場合に Cloudflare がアクセスをブロックしてくれるようになります。

Cloudflare
Cloudflare