2026年5月20日
2026年5月20日
WordPressにWAFを設定する方法|ModSecurity・Cloudflare・Wordfenceの導入と誤検知対策
WAF(Webアプリケーションファイアウォール)は、Web サイトへのリクエストを調べ、SQL インジェクションやクロスサイトスクリプティング(XSS)などの攻撃に典型的なパターンを遮断する仕組みです。WordPress 本体やプラグインの脆弱性が公表されてから更新するまでの間の攻撃を減らせますが、更新の代わりにはなりません。WAF は「更新・不要なプラグインの削除・強いパスワード」に重ねる防御として使います。
この記事のコマンドとパッケージ名は Ubuntu 24.04 LTS(libapache2-mod-security2 2.9.7、libnginx-mod-http-modsecurity、modsecurity-crs 3.3.5)の検証環境で2026年10月時点に動作を確認しています。Cloudflare と Wordfence の内容は、それぞれの公式ドキュメントで同じ時点の内容を確認しています。
どのWAFを使うか
| 方法 | 動く場所 | 向いている環境 | 注意点 |
|---|---|---|---|
| レンタルサーバーのWAF | サーバー会社のWebサーバー | 国内の共用レンタルサーバー | 多くはコントロールパネルでオン・オフするだけ。細かい除外設定はできないことが多い |
| ModSecurity + OWASP CRS | 自分のサーバーの Apache / Nginx | VPS・クラウドで root 権限がある | 導入と誤検知の調整に手間がかかる |
| Cloudflare WAF | Cloudflare のネットワーク(サーバーに届く前) | DNS を Cloudflare に向けられるサイト | 無料プランは使えるルールが限られる |
| Wordfence(プラグイン) | WordPress の PHP の中 | どの環境でも入れられる | サーバーの負荷は WordPress 側にかかる。無料版はルールの配信が遅れる |
レンタルサーバーを使っている場合は、まずコントロールパネルにある WAF 機能を有効にするのが手軽です。名称や設定場所は各社のマニュアルで確認してください。以下では、自分で設定する3つの方法を順に説明します。
導入前に: 今どんな攻撃が来ているかを見る
アクセスログから、攻撃によく使われる文字列を含むリクエストを数えると、WAF の効果を後で比べられます。ログの場所は環境に合わせて変えてください(Ubuntu の既定は Apache が /var/log/apache2/access.log、Nginx が /var/log/nginx/access.log)。
LOG=/var/log/nginx/access.log
# SQLインジェクションやパストラバーサルに典型的な文字列を含むリクエスト(直近30件)
grep -iE "union.*select|information_schema|\.\./|%2e%2e|etc/passwd|<script|%3cscript" "$LOG" | tail -30
# 上のリクエストの送信元IPアドレスを多い順に表示
grep -iE "union.*select|information_schema|\.\./|%2e%2e|etc/passwd|<script|%3cscript" "$LOG" \
| awk '{print $1}' | sort | uniq -c | sort -rn | head -20
# wp-login.php と xmlrpc.php への POST の回数
grep -E "POST /(wp-login|xmlrpc)\.php" "$LOG" | wc -l
ログイン画面への大量の POST(総当たり攻撃)は WAF のルールより、ログイン試行の制限で防ぐ方が効果的です。ブルートフォース攻撃の対策 も参照してください。
方法1: Apache に ModSecurity と OWASP CRS を入れる(Ubuntu)
ModSecurity は Apache・Nginx で動く WAF のエンジンで、OWASP CRS(Core Rule Set)はその上で使う汎用の検知ルール集です。Ubuntu では両方ともパッケージで入ります。
sudo apt update
sudo apt install libapache2-mod-security2 modsecurity-crs
# 推奨設定をコピーして有効な設定ファイルにする
sudo cp /etc/modsecurity/modsecurity.conf-recommended /etc/modsecurity/modsecurity.conf
# モジュールが有効か確認(インストール時に自動で有効になる)
sudo a2query -m security2
sudo apache2ctl configtest
sudo systemctl restart apache2
/etc/apache2/mods-available/security2.confが/etc/modsecurity/*.confと CRS(/usr/share/modsecurity-crs/owasp-crs.load)を読み込むため、上の操作だけで CRS も有効になります。- コピーした直後の
modsecurity.confはSecRuleEngine DetectionOnly(検知のみ。遮断しない)です。まずこのまま数日から1週間ほど運用し、正常な操作が攻撃と判定されていないかを確認します。
WordPress 用の除外を有効にする
CRS 3.x には、WordPress の管理画面の正常な操作(投稿本文の HTML など)を誤検知しないための除外ルールが含まれています。/etc/modsecurity/crs/crs-setup.conf の末尾に次を追加して有効にします。
SecAction \
"id:900130,\
phase:1,\
nolog,\
pass,\
t:none,\
setvar:tx.crs_exclusions_wordpress=1"
crs-setup.conf には同じ ID 900130 のコメントアウトされた例があります。そちらのコメントを外して使う場合は、WordPress 以外の行(crs_exclusions_drupal など)を削除してください。同じ ID を2か所で有効にすると Apache が起動しません。
検知の結果を確かめて遮断に切り替える
検知モードの間は、攻撃と判定されたリクエストが Apache のエラーログと監査ログ(/var/log/apache2/modsec_audit.log)に記録されます。どのルールが反応したかは、エラーログの [id "…"] で確認します。
# 反応したルールIDを多い順に表示
sudo grep -o '\[id "[0-9]*"\]' /var/log/apache2/error.log | sort | uniq -c | sort -rn | head -20
# 特定のルールが反応したリクエストのURIを確認
sudo grep 'id "941100"' /var/log/apache2/error.log | grep -o '\[uri "[^"]*"\]' | sort | uniq -c
正常な操作で反応しているルールがあれば、そのURIに限ってルールを外します。除外は /etc/modsecurity/crs/REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf に書きます(ID は 1〜99999 の範囲で、他と重ならない番号にします)。
# 例: 特定のプラグインの保存処理で 941100 が誤検知する場合
SecRule REQUEST_URI "@beginsWith /wp-admin/admin-ajax.php" \
"id:1000,phase:1,pass,nolog,ctl:ruleRemoveById=941100"
誤検知が落ち着いたら、遮断を有効にします。
sudo sed -i 's/^SecRuleEngine DetectionOnly/SecRuleEngine On/' /etc/modsecurity/modsecurity.conf
sudo apache2ctl configtest && sudo systemctl restart apache2
# 動作確認: SQLインジェクションに似たリクエストが 403 になり、通常のページは 200 のままか
curl -s -o /dev/null -w '%{http_code}\n' "https://example.com/?id=1%27%20or%201=1--"
curl -s -o /dev/null -w '%{http_code}\n' "https://example.com/"
検証環境では、1つ目が 403、2つ目が 200 になることを確認しています。
方法2: Nginx に ModSecurity と OWASP CRS を入れる(Ubuntu)
Nginx では ModSecurity v3(libmodsecurity)と Nginx 用の接続モジュールを使います。Ubuntu 24.04 では libnginx-mod-http-modsecurity パッケージで入ります(インストールすると /etc/nginx/modules-enabled/ にモジュールの読み込み設定が追加されます)。
sudo apt update
sudo apt install libnginx-mod-http-modsecurity modsecurity-crs
# パッケージに付属する設定ファイルを Nginx 用の場所にコピーする
sudo mkdir -p /etc/nginx/modsec
sudo cp /usr/share/nginx/docs/modsecurity/modsecurity.conf /usr/share/nginx/docs/modsecurity/unicode.mapping /etc/nginx/modsec/
ModSecurity の設定と CRS をまとめて読み込むファイルを作ります(sudo cat > ファイル はリダイレクトに root 権限が及ばず失敗するので sudo tee を使います)。
sudo tee /etc/nginx/modsec/main.conf > /dev/null <<'EOF'
Include /etc/nginx/modsec/modsecurity.conf
Include /etc/modsecurity/crs/crs-setup.conf
Include /etc/modsecurity/crs/REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf
Include /usr/share/modsecurity-crs/rules/*.conf
Include /etc/modsecurity/crs/RESPONSE-999-EXCLUSION-RULES-AFTER-CRS.conf
EOF
サイトの server ブロックで有効にします。
server {
server_name example.com;
# (既存の listen・root・location などはそのまま)
modsecurity on;
modsecurity_rules_file /etc/nginx/modsec/main.conf;
}
sudo nginx -t
sudo systemctl reload nginx
/etc/nginx/modsec/modsecurity.confも最初はSecRuleEngine DetectionOnlyです。Apache と同じく、WordPress 用の除外(crs-setup.confへの追記)と検知結果の確認をしてからOnに変えます。- 反応したルールは
/var/log/nginx/error.logの[id "…"]と、監査ログ/var/log/nginx/modsec_audit.logで確認します。
Nginx での設定例は NginxでModSecurityを使う方法 でも説明しています。
方法3: Cloudflare WAF を使う
サイトの DNS を Cloudflare に向け、プロキシ(オレンジの雲)を有効にしていると、リクエストはサーバーに届く前に Cloudflare の WAF を通ります。導入の手順は CloudflareのWAFを設定する方法 を参照してください。
Cloudflare の公式ドキュメント(2026年10月時点)によるプランごとの違いは次のとおりです。
| 機能 | Free | Pro / Business | Enterprise |
|---|---|---|---|
| カスタムルールの数 | 5 | 20 / 100 | 1,000 |
| Cloudflare Free Managed Ruleset | 使える | 使える | 使える |
| Cloudflare Managed Ruleset・OWASP Core Ruleset | 使えない | 使える | 使える |
無料プランでもカスタムルールで、WordPress への典型的な攻撃を絞り込めます。例として、管理者の IP アドレス以外からのログイン画面へのアクセスを止めるルールの式です(203.0.113.0/24 は自分の環境に置き換えます)。
(http.request.uri.path eq "/wp-login.php" and not ip.src in {203.0.113.0/24})
アクションは「ブロック」か、人間なら通過できる「マネージドチャレンジ」を選びます。固定 IP アドレスが無い場合は、IP アドレスで絞らず、ログイン画面全体にマネージドチャレンジをかける方法もあります。
Cloudflare を通さずにサーバーの IP アドレスへ直接アクセスされると WAF は効きません。サーバー側のファイアウォールで、Web のポートを Cloudflare の IP アドレス範囲だけに許可すると、迂回を防げます。
方法4: Wordfence のファイアウォールを使う
Wordfence はプラグインの中でファイアウォールを動かします。サーバーの設定を変えられない環境でも使えるのが利点です。
# WP-CLI でインストールして有効化する場合
wp plugin install wordfence --activate
- 有効化した直後は「Basic WordPress Protection」で、WordPress とほかのプラグインが読み込まれた後にファイアウォールが動きます。
- Wordfence の「Firewall」画面にある最適化(Optimize)の案内に従うと「Extended Protection」になり、PHP の
auto_prepend_fileでwordfence-waf.phpが WordPress より先に読み込まれます。脆弱なプラグインのコードが動く前に遮断できるため、こちらに切り替えることを Wordfence は推奨しています。 - 最適化では、サーバーの種類に応じて
.htaccess・.user.ini・php.iniのどれかが書き換えられます。途中で表示される元のファイルのバックアップを保存してから進めてください。サーバーがauto_prepend_fileの変更を許可していない場合は「Basic WordPress Protection」のままになります。 - 無料版のファイアウォールのルールとマルウェアの定義は、有料版(Premium)より30日遅れて配信されます。
- ファイアウォールが原因でサイトが表示されなくなったときは、「Remove Extended Protection」で設定を戻すか、Wordfence のドキュメントにある
WFWAF_ENABLED定数で一時的に無効にします。
初期設定の詳細は Wordfenceの初期設定 を参照してください。
誤検知でWordPressの操作が403になったとき
WAF を入れた後に、投稿の保存やプラグインの設定の保存、画像のアップロードが 403 エラーで失敗することがあります。投稿本文の HTML やコードが攻撃パターンに似ているためです。
- エラーが起きた時刻と URL(例:
/wp-json/wp/v2/posts/123、/wp-admin/admin-ajax.php)を控える - WAF のログで、その時刻に反応したルール ID を探す(ModSecurity はエラーログ、Cloudflare はダッシュボードのセキュリティイベント、Wordfence は Live Traffic の記録)
- そのルールを、その URL に限って除外する(WAF 全体は止めない)
- 除外した内容と理由を記録しておく
レンタルサーバーの WAF で除外ができない場合は、作業中だけ WAF を止めて保存し、終わったら戻す運用になります。止めたまま忘れないよう注意してください。
まとめ
- WAF は更新や不要なプラグインの削除の代わりではなく、重ねて使う防御
- 自分のサーバーなら ModSecurity と OWASP CRS をパッケージで入れ、
DetectionOnlyで誤検知を確認してからOnにする。WordPress 用の除外(crs_exclusions_wordpress)を有効にする - Cloudflare は無料プランでもカスタムルール5個と Free Managed Ruleset が使える。サーバーへの直接アクセスも閉じる
- Wordfence は「Extended Protection」に最適化して使う。無料版のルールは30日遅れ
- 403 の誤検知は、ログでルール ID を特定し、その URL に限って除外する