0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Cloudflare無料プランのWAFでWordPressを守る5つのカスタムルール(式コピペOK)

0
Posted at

こんな方向け: Cloudflare の無料プランに WordPress サイトを載せたものの、WAF は「とりあえず有効」のままの Web 担当者・制作会社の方。
この記事で解決すること: 無料プランの カスタムルール 5 本(+レート制限 1 本)を、WordPress でよく狙われる入口に割り振る式(式エディタにそのまま貼れる形)と手順がわかります。

はじめに:無料プランで使える WAF の範囲

WAF(Web Application Firewall)= Web サイトへのリクエストを中身で判定して、怪しいものを手前で止める仕組みです。Cloudflare では、プロキシ(オレンジ雲)を有効にしたホスト名への通信がエッジ(Cloudflare 側)で判定されます。

無料(Free)プランの主な制約は次のとおりです(Cloudflare 公式ドキュメントより)。

項目 Free プランの範囲
カスタムルール 5 本
正規表現(matches 演算子) 使えない(Business 以上)
アクション Log 以外(Block / Managed Challenge / Skip など)
カスタムリスト(IP リスト) 1 個・合計 1 万件まで
レート制限ルール 1 本・10 秒単位で数えて 10 秒遮断・IP 単位・条件に使えるのは Path と Verified Bot のみ
マネージドルール Cloudflare Free Managed Ruleset(影響が大きく悪用が広がった脆弱性向け)

正規表現が使えないので、式は eq / in / wildcard(* のパターン一致)で書きます。wildcard は大文字小文字を区別しない(公式)ため、/WP-LOGIN.PHP のような表記ゆれも拾えます。

先に確認したい 2 つの前提

  1. WAF は Cloudflare を経由した通信にしか効きません。 オリジンの IP へ直接アクセスされると全ルールが素通りです。オリジン側で Cloudflare 以外からの 80/443 を受けない設定まで済ませてください(関連記事参照)。
  2. ルールは上から順に評価され、Block に当たるとそこで止まります。 あとから Skip ルールを足すときは並び順に注意してください。

準備:管理者 IP のリストを作る

ルール 1 で使う「管理画面に入ってよい IP」を、カスタムリストに登録しておきます。

  • アカウントの Manage Account → Configurations → Lists(UI 更新で場所が変わることがあります)から IP リストを作成
  • 名前は admin_ips とします(英小文字・数字・アンダースコア)
  • 事務所の固定 IP、VPN の出口 IP、制作会社の固定 IP などを登録

式からは $admin_ips で参照します。

ip.src in $admin_ips

無料プランではリストは 1 個までです。そのため、用途を「管理者の IP」に絞り、ほかのルールではリストを使わない設計にしています。

ルール 1:ログイン画面・管理画面を管理者 IP だけに絞る

総当たり攻撃(パスワードを大量に試す攻撃)を入口で止めます。admin-ajax.php と admin-post.php は 公開ページ側のフォームやプラグインも呼び出す ので除外しないと、サイトの一部機能が壊れます。

(
  http.request.uri.path wildcard "/wp-login.php"
  or http.request.uri.path wildcard "/wp-admin"
  or http.request.uri.path wildcard "/wp-admin/*"
)
and not http.request.uri.path wildcard "/wp-admin/admin-ajax.php"
and not http.request.uri.path wildcard "/wp-admin/admin-post.php"
and not ip.src in $admin_ips
  • アクション: Block
  • WordPress をサブディレクトリ(例 /blog/)に置いている場合は、パスの先頭を /blog/wp-login.php のように書き換えます
  • 会員登録・パスワードリセットで一般利用者も wp-login.php を使うサイトでは、このルールは使えません。次の代替案を使ってください

固定 IP が無い場合の代替案

在宅勤務などで管理者の IP が固定できないときは、国で絞ったうえで Block ではなく Managed Challenge(Cloudflare が人間かどうかを自動判定し、必要なときだけ確認画面を出す)にします。

(
  http.request.uri.path wildcard "/wp-login.php"
  or http.request.uri.path wildcard "/wp-admin"
  or http.request.uri.path wildcard "/wp-admin/*"
)
and not http.request.uri.path wildcard "/wp-admin/admin-ajax.php"
and not http.request.uri.path wildcard "/wp-admin/admin-post.php"
and not ip.src.country in {"JP"}

ip.src.country は ISO 3166-1 の 2 文字コードです(古い ip.geoip.country は非推奨)。海外出張・海外 VPN 経由では確認画面が出ます。

ルール 2:xmlrpc.php を止める

xmlrpc.php は外部アプリから WordPress を操作するための古い入口です。使っていないのに開いたままだと、パスワード総当たりの的になり続けます。

http.request.uri.path wildcard "/xmlrpc.php"
  • アクション: Block
  • Jetpack・WordPress モバイルアプリなど XML-RPC を使う連携があるなら止めないでください。 不明なら Security Events(後述)で数日確認してから有効にします

ルール 3:uploads 内の PHP と機密ファイルへのアクセスを止める

画像置き場の wp-content/uploads/ に PHP が置かれて実行される被害は、改ざんの典型例です。あわせて、外から見えてはいけないファイルへのアクセスも止めます。

http.request.uri.path wildcard "/wp-content/uploads/*.php"
or http.request.uri.path wildcard "/wp-config.php*"
or http.request.uri.path wildcard "/.env*"
or http.request.uri.path wildcard "/.git/*"
or http.request.uri.path wildcard "/wp-content/debug.log"
or http.request.uri.path wildcard "*.sql"
or http.request.uri.path wildcard "*.bak"
  • アクション: Block
  • 証明書の自動更新で使う /.well-known/ はこの式に当たらないので、Let's Encrypt の更新は妨げません
  • これは 追加の層 です。Cloudflare を外した瞬間に無防備にならないよう、サーバー側でも uploads の PHP 実行禁止と機密ファイルの配置見直しを行ってください

ルール 4:ユーザー名の列挙を止める

WordPress では、ログイン ID(ユーザー名)が外から推測される経路がいくつかあります。代表的な 3 つの経路を、ログインしていない訪問者に限って 止めます。

(
  http.request.uri.path wildcard "/wp-json/wp/v2/users*"
  or http.request.uri.query wildcard "*rest_route=/wp/v2/users*"
  or http.request.uri.query wildcard "*rest_route=%2fwp%2fv2%2fusers*"
  or http.request.uri.query wildcard "author=*"
  or http.request.uri.query wildcard "*&author=*"
)
and not http.cookie contains "wordpress_logged_in_"
  • アクション: Block
  • ブロックエディタはログイン中に /wp-json/wp/v2/users を呼ぶため、ログイン Cookie がある通信は除外しています
  • Cookie は利用者側で偽装できます。 このルールは「大量の機械的な列挙を手前で減らす」ためのものです。WordPress 側の対策(REST API のユーザー一覧の非公開化など)を別途行ってください

ルール 5:緊急用に 1 枠空けておく

5 本すべてを常設で埋めず、プラグインの脆弱性が公表されてすぐ更新できないときに 更新までのつなぎとして該当の入口だけを塞ぐ 枠を残しておきます。

http.request.uri.path wildcard "/wp-content/plugins/example-plugin/*"
  • アクション: Block(example-plugin は実際のプラグインのディレクトリ名に置き換えます)
  • ディレクトリごと止めると公開ページの CSS・JS も止まるので、可能なら公式アドバイザリが示すパスに絞ります
  • 更新が済んだら必ず無効化・削除します。 つなぎのルールは更新の代わりになりません

おまけ:レート制限 1 本は wp-login.php に

カスタムルールとは別枠で、レート制限ルールが 1 本使えます。無料プランでは条件に Path しか使えないので、ログイン画面に充てるのが素直です。

http.request.uri.path eq "/wp-login.php"
  • 数える単位: IP(無料プランはこれのみ)
  • しきい値の例: 10 秒あたり 5 リクエストを超えたら Block(遮断時間は 10 秒固定)
  • 遮断は 10 秒で解けるので 速度を落とす補助 です。サーバー側のレート制限・ログイン試行制限と組み合わせてください

効いているかを確認する

1. 自分のサイトに curl で確認する

必ず自社で管理しているサイトだけで 確認してください。管理者 IP 以外の回線(スマホのテザリングなど)から実行します。

SITE="https://example.com"   # 自社サイトに置き換える
for p in /xmlrpc.php /wp-login.php /wp-content/uploads/test.php /.env "/?author=1" /wp-json/wp/v2/users; do
  printf '%-32s %s\n' "$p" "$(curl -s -o /dev/null -w '%{http_code}' "$SITE$p")"
done

Block に当たったものは 403 が返ります。トップページや画像など通常のページが 200 のままであることも、あわせて確認してください。

2. Security Events で誤検知を見る

Security → Events(Security Analytics)でルールごとに止めた通信を見られます。有効化直後の数日は 正規の利用者や連携が止まっていないか を確認します。

3. ルールを API で棚卸しする

経緯不明のルールが残りがちなので、定期的に一覧を書き出します。WAF の読み取り権限だけの API トークンを使います。

# CF_API_TOKEN と ZONE_ID は環境変数で渡す(スクリプトに直書きしない)
curl -s "https://api.cloudflare.com/client/v4/zones/${ZONE_ID}/rulesets/phases/http_request_firewall_custom/entrypoint" \
  -H "Authorization: Bearer ${CF_API_TOKEN}" \
  | jq -r '.result.rules[] | [.enabled, .action, .description] | @tsv'

無効のまま放置されたルールや緊急用枠の消し忘れを確認します。

まとめ

枠 内容 アクション
ルール 1 wp-login.php・wp-admin を管理者 IP に限定(代替: 国外は Managed Challenge) Block
ルール 2 xmlrpc.php Block
ルール 3 uploads 内 PHP・機密ファイル Block
ルール 4 未ログイン時のユーザー名列挙 Block
ルール 5 緊急用の空き枠(更新までのつなぎ) Block
レート制限 wp-login.php を IP 単位で 10 秒 5 回まで Block
  • オリジンへの直接アクセスを塞ぐところまでが 1 セットです
  • エッジのルールはサーバー側の対策に 重ねる層 で、WordPress・プラグインの更新の代わりにはなりません

関連記事


本記事のような設定の抜けは、Web サイトを 9 つの守りでまるごと守るサイトドックでまとめて対策できます → https://sitedock.jp

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?