2026年6月20日

2026年6月20日

NginxにModSecurity(WAF)を設定してWordPressを守る手順|Ubuntu・Debian+OWASP CRS

ModSecurity は、Web サーバーに組み込んで使うオープンソースの WAF(Web アプリケーションファイアウォール)です。リクエストの中身をルールで検査し、SQL インジェクションやクロスサイトスクリプティング(XSS)などの攻撃と思われる通信を記録したり、遮断したりします。ルールには、OWASP が公開している Core Rule Set(CRS)を使うのが一般的です。

この記事では、Ubuntu・Debian の Nginx に ModSecurity を入れ、WordPress サイトで使うまでの手順を説明します。

NginxとApacheではModSecurityの入れ方が違う

ModSecurity には2つの系統があり、Nginx で使えるのは v3 だけです。

系統 中身 使える Web サーバー
ModSecurity v2(mod_security2) Apache のモジュール Apache
ModSecurity v3(libmodsecurity) Web サーバーから独立したライブラリ Nginx(ModSecurity-nginx コネクターを使う)

Nginx で使うには、ライブラリの libmodsecurity と、Nginx とライブラリをつなぐ「ModSecurity-nginx コネクター」(Nginx の動的モジュール)の2つが必要です。Apache 用の手順(apxs でのビルドや mod_security パッケージ)は Nginx では使えません。Apache の場合は Apacheのmod_security(ModSecurity)でWAFを設定する方法 を参照してください。

ModSecurity の開発は、2024年に Trustwave から OWASP に移りました。2026年10月時点のリポジトリは GitHub の owasp-modsecurity/ModSecurity(最新は v3.0.17)と owasp-modsecurity/ModSecurity-nginx(最新は v1.0.4)です。古い記事にある SpiderLabs のリポジトリは移行前のものです。

この記事の前提

  • OS は Ubuntu 24.04 以降、または Debian 12・13
  • Nginx は OS 標準のパッケージ(apt install nginx)で入れたもの
  • WordPress は Nginx と PHP-FPM で動いている

OS のパッケージには、OS 標準の Nginx 向けにビルドされたコネクター(libnginx-mod-http-modsecurity)と CRS(modsecurity-crs)があります。2026年10月時点で、パッケージの CRS は 3.3 系です(Ubuntu 24.04 は 3.3.5、Debian 13 は 3.3.7)。Ubuntu では universe リポジトリのパッケージです。

nginx.org のリポジトリから入れた Nginx では、OS のコネクターパッケージは使えません。その場合は、ModSecurity-nginx の README に従って、使っている Nginx と同じバージョンのソースから動的モジュールをビルドする必要があります。

手順1:パッケージを入れる

sudo apt update
sudo apt install libnginx-mod-http-modsecurity modsecurity-crs

コネクターのパッケージは、次のファイルを置きます。

ファイル 役割
/etc/nginx/modules-enabled/50-mod-http-modsecurity.conf モジュールを読み込む設定
/etc/nginx/modsecurity.conf ModSecurity 本体の設定(動作モード・ログなど)
/etc/nginx/modsecurity_includes.conf Nginx から読み込むルールの入口
/etc/nginx/unicode.mapping 文字コード変換用の表

モジュールが読み込まれ、設定に誤りがないことを確認します。

ls /etc/nginx/modules-enabled/
sudo nginx -t

nginx -t で syntax is ok と test is successful が出れば問題ありません(Nginxの設定文法チェックコマンド:nginx -t・-Tの使い方とエラーメッセージの読み方)。

手順2:CRSを読み込む設定を書く

/etc/nginx/modsecurity_includes.conf は、初期状態では本体の設定だけを読み込み、CRS の行はコメントになっています。

include modsecurity.conf
#include /usr/share/modsecurity-crs/owasp-crs.load

コメントになっている owasp-crs.load は Apache 向けに書かれたファイルで、ModSecurity v3 が対応していない IncludeOptional を使っています(パッケージの README.Debian にも、そのままでは動かないと書かれています)。そこで、同じ内容を Include で書き直します。/etc/nginx/modsecurity_includes.conf を次の内容にします。

include 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
  • crs-setup.conf:CRS の基本設定(検査の厳しさなど)
  • REQUEST-900-... と RESPONSE-999-...:自分で書く除外ルールの置き場所
  • rules/*.conf:CRS 本体のルール

手順3:WordPress用の除外設定を有効にする

CRS 3.3 には、WordPress の管理画面などで起きやすい誤検知を避けるための除外ルール(REQUEST-903.9002-WORDPRESS-EXCLUSION-RULES.conf)が含まれています。これは既定では無効なので、/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 の行だけを残し、末尾への追加はしません(同じ設定を二重に書かないようにします)。

手順4:WordPressのserverブロックで有効にする

WordPress サイトの server ブロック(HTTPS で公開している場合は listen 443 のブロック)に、次の2行を追加します。

server {
    listen 443 ssl;
    server_name example.com;

    modsecurity on;
    modsecurity_rules_file /etc/nginx/modsecurity_includes.conf;

    # ... 既存の root・location・PHP-FPM の設定 ...
}

設定を確認して反映します。

sudo nginx -t && sudo systemctl reload nginx

手順5:検知のみのモードで動作を確かめる

パッケージの /etc/nginx/modsecurity.conf は SecRuleEngine DetectionOnly(検知して記録するが、遮断はしない)になっています。最初はこのまま運用し、誤検知がないかを確かめます。

攻撃に見えるリクエストを送ってみます(example.com は自分のサイトに置き換えます)。

curl -s -o /dev/null -w '%{http_code}\n' "https://example.com/?q=%3Cscript%3Ealert(1)%3C%2Fscript%3E"

検知のみのモードなので、ステータスは通常どおり 200 が返ります。ModSecurity の記録(監査ログ)は /var/log/nginx/modsec_audit.log に出ます。

sudo tail -n 50 /var/log/nginx/modsec_audit.log

ModSecurity: Warning. で始まる行と、[id "941100"] のようなルール ID が記録されていれば、ModSecurity と CRS が動いています。nginx -t の出力にある rules loaded inline/local/remote: 0/926/0 のような表示でも、読み込まれたルールの数を確認できます(数は CRS のバージョンで変わります)。

この記事の手順は、Debian 13 のパッケージ(ModSecurity-nginx 1.0.3・CRS 3.3.7)で、検知のみのモードで記録されること、手順6の除外が効くこと、手順7で 403 が返ることを確認しています。

手順6:誤検知を確認して除外する

検知のみのモードのまま、数日から1週間ほど普段どおりサイトを使います。特に次の操作は誤検知が出やすいので、一通り試します。

  • ブロックエディターでの投稿の保存・公開(REST API への送信)
  • メディアのアップロード
  • プラグインやテーマの設定画面での保存
  • お問い合わせフォームの送信、コメントの投稿

監査ログに記録されたルール ID を、多い順に数えます。

sudo grep -o '\[id "[0-9]*"\]' /var/log/nginx/modsec_audit.log | sort | uniq -c | sort -rn | head -20

自分の操作で記録されたものは誤検知です。ログの同じ記録にある URI(/wp-json/... や /wp-admin/... など)と、どの項目(ARGS:... など)が引っかかったかを確認し、範囲を絞って除外します。たとえば、ある管理画面の URL でだけルール 942100 を外す場合は、/etc/modsecurity/crs/REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf に次のように書きます。

SecRule REQUEST_FILENAME "@beginsWith /wp-admin/admin-ajax.php" \
    "id:1001,\
    phase:1,\
    pass,\
    nolog,\
    ctl:ruleRemoveById=942100"
  • ルール ID(942100)と URL は、自分のログに出たものに置き換えます
  • 自分で書くルールの id には、他と重ならない番号(例:1000番台)を使います
  • サイト全体でルールを外す(SecRuleRemoveById)と守りが弱くなるため、URL や項目で範囲を絞ります

除外を書いたら sudo nginx -t && sudo systemctl reload nginx で反映し、同じ操作でログに記録されなくなったことを確認します。

手順7:遮断モードに切り替える

誤検知が出なくなったら、/etc/nginx/modsecurity.conf の動作モードを On に変えます。

sudo sed -i 's/^SecRuleEngine DetectionOnly/SecRuleEngine On/' /etc/nginx/modsecurity.conf
sudo nginx -t && sudo systemctl reload nginx

手順5と同じリクエストを送り、403 が返れば遮断できています。

curl -s -o /dev/null -w '%{http_code}\n' "https://example.com/?q=%3Cscript%3Ealert(1)%3C%2Fscript%3E"

切り替えた後も、しばらくは監査ログを確認してください。管理画面で保存できない、フォームが送れないといった連絡があれば、手順6と同じ方法で原因のルールを探します(mod_securityが原因の403エラーの解決方法【WAF誤検知の対処】)。

運用での注意

  • アップロードの上限:パッケージの modsecurity.conf では、リクエスト本文の上限 SecRequestBodyLimit が 13107200(約12.5MB)で、超えると拒否する設定(SecRequestBodyLimitAction Reject)になっています。Nginx の client_max_body_size や PHP の上限をこれより大きくしている場合は、SecRequestBodyLimit も合わせて上げます
  • ログの量:/var/log/nginx/modsec_audit.log は、Nginx パッケージのログローテーション設定(/var/log/nginx/*.log が対象)で一緒にローテーションされます。攻撃の多いサイトでは容量に気をつけます
  • 処理の負荷:すべてのリクエストを検査するため、CPU の使用量は増えます。導入前後でサーバーの負荷を比べてください
  • 他の対策との役割分担:ModSecurity は1つ1つのリクエストの中身を検査する仕組みです。ログイン試行の繰り返しのように、回数で判断する攻撃には fail2ban などを組み合わせます(ModSecurity と fail2ban の違いと使い分け|WordPressサーバーでの役割分担と併用の手順)

まとめ

  • Nginx で使えるのは ModSecurity v3(libmodsecurity)で、ModSecurity-nginx コネクターが必要。Apache 用の手順は使えない
  • Ubuntu・Debian では libnginx-mod-http-modsecurity と modsecurity-crs のパッケージで入れられる
  • modsecurity_includes.conf に CRS を Include で書き、crs-setup.conf で WordPress 用の除外(tx.crs_exclusions_wordpress=1)を有効にする
  • 最初は DetectionOnly で誤検知を確認・除外してから、SecRuleEngine On に切り替える

関連記事:

お気軽にご相談ください

お見積りへ お問い合わせへ
▲