mod_security + OWASP CRS + fail2ban環境を構築する【OpenSUSE】
Setting up mod_security + OWASP CRS + fail2ban on OpenSUSE
はじめに
前回の記事ではPFによるシンプルなブルートフォース攻撃防止の方法について説明しましたが、今回はより高度な攻撃検知・防御システムの構築方法について解説します。
従来のブルートフォース防御は、連続したログイン試行や大量アクセスを回数ベースで検知する仕組みとなっています。しかし、SQLインジェクションやXSS、ディレクトリトラバーサルといった攻撃は、たった1回のリクエストで成功する可能性があります。また、複数のIPアドレスを使った分散型攻撃では、各IPからの試行回数は少なくても、全体としては大規模な攻撃になります。このような高度な攻撃に対応するには、リクエストの内容そのものを検査する仕組みが必要です。
今回、この検査するルールセットとしてOWASP CRSを使用しました。OWASP Core Rule Setは、包括的な攻撃検知ルールセットで、SQLインジェクション・XSS・リモートコード実行・ファイルインクルージョンなど、主要な攻撃パターンを網羅しています。これらのルールを独自に作成するにはかなりの労力が必要ですが、OWASP CRSを使えば、誰でも簡単に世界標準のセキュリティ対策を導入できます。ライセンスもASLv2となっており商用でも利用可能です。
本記事では、ApacheにModSecurity2を組み込み、OWASP CRSで攻撃を検知し、fail2banで攻撃元IPを自動的にiptablesでブロックするシステムを構築していきます。
実行環境
OS: openSUSE Tumbleweed 20251127
Webサーバー: Apache 2.4.65 (prefork MPM)
ハードウェア: Intel Xeon E5-2650L v4 (14コア) / メモリ 32GB
運用サイト: WordPress、静的サイト、Webアプリなど
※基本的にどのディストリビューションでもこれらの実装が可能です。設定ファイルの場所など、細かい差異はマニュアルや公式ページを参照してください。
インストール
実際にシステムを構築していきます。
はじめに、必要なパッケージをインストールします。openSUSEではzypperコマンドを使用してパッケージ管理を行います。
# mod_security2とOWASP CRSのインストール
sudo zypper install apache2-mod_security2 owasp-modsecurity-crs
# fail2banのインストール
sudo zypper install fail2ban
# 依存パッケージの確認
rpm -qa | grep mod_security
rpm -qa | grep owasp
インストールが完了したら、正常にパッケージがインストールされているか確認します。OpenSUSEの場合、rpmコマンドでパッケージ情報を表示することで、バージョンやビルド情報を確認できます。
$ rpm -qi apache2-mod_security2
Name : apache2-mod_security2
Version : 2.9.12
Release : 1.2
Architecture: x86_64
Install Date: Tue 11 Nov 2025 02:01:44 PM JST
Group : Productivity/Networking/Web/Servers
Size : 1045515
License : Apache-2.0
Signature : RSA/SHA512, Fri 10 Oct 2025 01:07:25 AM JST, Key ID 35a2f86e29b700a4
Source RPM : apache2-mod_security2-2.9.12-1.2.src.rpm
Build Date : Thu 07 Aug 2025 03:25:52 AM JST
Build Host : reproducible
Packager : https://bugs.opensuse.org
Vendor : openSUSE
URL : https://www.modsecurity.org/
Summary : Web Application Firewall for Apache httpd
Description :
ModSecurity is an intrusion detection and prevention
engine for web applications (or a web application firewall). Operating
as an Apache Web server module or standalone, the purpose of
ModSecurity is to increase web application security, protecting web
applications from known and unknown attacks.
Distribution: openSUSE Tumbleweed
パッケージが正常にインストールされていることを確認したら、Apacheのモジュールとしてmod_security2を有効化します。
# mod_security2モジュールの有効化
sudo a2enmod security2
mod_security2の基本設定
OpenSUSEでは、conf.d/ ディレクトリに配置された .conf ファイルが自動的に読み込まれるので、元のパッケージファイルを直接編集するよりも、カスタム設定ファイルを作成する方が管理しやすいです。
基本的にはデフォルト設定のままでも動作しますが、WordPressなど大きなファイルアップロードが必要な場合や、パフォーマンスをチューニングしたい場合は、カスタム設定を作成することをお勧めします。以下はカスタム設定の例です。
sudo vim /etc/apache2/conf.d/mod_security2_custom.conf
<IfModule mod_security2.c>
SecRuleEngine On
SecRequestBodyLimit 13107200
SecRequestBodyNoFilesLimit 131072
# レスポンスボディ検査を有効化(デフォルトはOff)
SecResponseBodyAccess On
SecResponseBodyMimeType text/plain text/html text/xml application/json
SecResponseBodyLimit 524288
# 作業ディレクトリの明示的指定
SecTmpDir /var/lib/mod_security/tmp
SecDataDir /var/lib/mod_security/data
SecUploadDir /var/lib/mod_security/upload
SecUploadKeepFiles Off
# 監査ログ設定の上書き(fail2ban連携用)
SecAuditEngine RelevantOnly
SecAuditLogRelevantStatus "^(?:5|4(?!04))"
SecAuditLogParts ABIJDEFHZ
SecAuditLogType Serial
SecAuditLog /var/log/apache2/modsec_audit.log
# デバッグログ(本番では0に設定)
SecDebugLog /var/log/apache2/modsec_debug.log
SecDebugLogLevel 0
# パフォーマンスチューニング
SecPcreMatchLimit 100000
SecPcreMatchLimitRecursion 100000
# OWASP CRS読み込み
IncludeOptional /etc/apache2/mod_security2.d/*.conf
IncludeOptional /etc/apache2/mod_security2.d/rules/*.conf
</IfModule>
カスタムした設定ファイルの内容としましては
SecRuleEngine On → 実際に攻撃をブロックする状態
SeSecRequestBodyLimit 13107200 → 検査対象とするリクエストボディの最大サイズを13MBに設定
※好みで変更してください
SecResponseBodyAccess On → レスポンスボディの検査を有効化
SecResponseBodyMimeType text/plain text/html text/xml application/json → 検査対象とするMIMEタイプ
※画像や動画などのバイナリファイルを検査してもパフォーマンスの無駄になるため
SecAuditEngine RelevantOnly → 攻撃が検知された場合にのみ詳細なログを記録
SecAuditLogRelevantStatus “^(?:5|4(?!04))” → 5xx系のエラーと404以外の4xx系エラーを記録対象
チューニングとして、SecPcreMatchLimitとSecPcreMatchLimitRecursionを設定しています。これらは正規表現マッチングの上限値で、複雑なパターンマッチングを行う際にCPUを使いすぎないようにするための設定です。
設定の反映と確認を行います。
sudo apachectl configtest
# 問題なければ
sudo systemctl restart apache2
OWASP CRSの配置確認
OpenSUSEのパッケージでは、ルールが /usr/share/owasp-modsecurity-crs/rules/ に配置され、/etc/apache2/mod_security2.d/rules/ からシンボリックリンクされています
$ ls /etc/apache2/mod_security2.d/rules/
iis-errors.data REQUEST-934-APPLICATION-ATTACK-GENERIC.conf
java-classes.data REQUEST-941-APPLICATION-ATTACK-XSS.conf
java-code-leakages.data REQUEST-942-APPLICATION-ATTACK-SQLI.conf
java-errors.data REQUEST-943-APPLICATION-ATTACK-SESSION-FIXATION.conf
lfi-os-files.data REQUEST-944-APPLICATION-ATTACK-JAVA.conf
php-config-directives.data REQUEST-949-BLOCKING-EVALUATION.conf
php-errors.data RESPONSE-950-DATA-LEAKAGES.conf
php-errors-pl2.data RESPONSE-951-DATA-LEAKAGES-SQL.conf
php-function-names-933150.data RESPONSE-952-DATA-LEAKAGES-JAVA.conf
php-function-names-933151.data RESPONSE-953-DATA-LEAKAGES-PHP.conf
php-variables.data RESPONSE-954-DATA-LEAKAGES-IIS.conf
REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf.example RESPONSE-955-WEB-SHELLS.conf
REQUEST-901-INITIALIZATION.conf RESPONSE-959-BLOCKING-EVALUATION.conf
REQUEST-905-COMMON-EXCEPTIONS.conf RESPONSE-980-CORRELATION.conf
REQUEST-911-METHOD-ENFORCEMENT.conf RESPONSE-999-EXCLUSION-RULES-AFTER-CRS.conf.example
REQUEST-913-SCANNER-DETECTION.conf restricted-files.data
REQUEST-920-PROTOCOL-ENFORCEMENT.conf restricted-upload.data
REQUEST-921-PROTOCOL-ATTACK.conf scanners-user-agents.data
REQUEST-922-MULTIPART-ATTACK.conf sql-errors.data
REQUEST-930-APPLICATION-ATTACK-LFI.conf ssrf.data
REQUEST-931-APPLICATION-ATTACK-RFI.conf unix-shell.data
REQUEST-932-APPLICATION-ATTACK-RCE.conf web-shells-php.data
REQUEST-933-APPLICATION-ATTACK-PHP.conf windows-powershell-commands.data
CRS設定ファイルはデフォルトですべてコメントアウトされています。コメントアウトされている場合、OWASP CRSは内部のデフォルト値を使用します。デフォルト値は以下のようになっています。
Paranoia Level: 1(デフォルト)
Inbound閾値: 5(Critical攻撃1回でブロック)
Outbound閾値: 4
許可HTTPメソッド: GET HEAD POST OPTIONS
Early Blocking: 無効
チューニングが必要な場合は、以下のような箇所のコメントを外して有効化します。Paranoia Levelは検知の厳格さを制御するパラメータで、1が最も緩く、4が最も厳格です。最初は1から始めて、誤検知が少ないことを確認してから徐々に上げていくことをお勧めします。
sudo vim /etc/apache2/mod_security2.d/modsecurity-crf-setup.conf
#Paranoia Level設定
# コメントを外して有効化
SecAction \
"id:900000,\
phase:1,\
pass,\
t:none,\
nolog,\
tag:'OWASP_CRS',\
ver:'OWASP_CRS/4.10.0',\
setvar:tx.blocking_paranoia_level=1"
# 検知専用Paranoia Level
SecAction \
"id:900001,\
phase:1,\
pass,\
t:none,\
nolog,\
tag:'OWASP_CRS',\
ver:'OWASP_CRS/4.10.0',\
setvar:tx.detection_paranoia_level=1"
# Anomaly Score閾値
SecAction \
"id:900110,\
phase:1,\
pass,\
t:none,\
nolog,\
tag:'OWASP_CRS',\
ver:'OWASP_CRS/4.10.0',\
setvar:tx.inbound_anomaly_score_threshold=5,\
setvar:tx.outbound_anomaly_score_threshold=4"
# 許可HTTPメソッド
SecAction \
"id:900200,\
phase:1,\
pass,\
t:none,\
nolog,\
tag:'OWASP_CRS',\
ver:'OWASP_CRS/4.10.0',\
setvar:'tx.allowed_methods=GET HEAD POST OPTIONS'"
# 許可Content-Type
SecAction \
"id:900220,\
phase:1,\
pass,\
t:none,\
nolog,\
tag:'OWASP_CRS',\
ver:'OWASP_CRS/4.10.0',\
setvar:'tx.allowed_request_content_type=|application/x-www-form-urlencoded| |multipart/form-data| |text/xml| |application/xml| |application/soap+xml| |application/json|'"
# Early Blocking有効化
SecAction \
"id:900120,\
phase:1,\
pass,\
t:none,\
nolog,\
tag:'OWASP_CRS',\
ver:'OWASP_CRS/4.10.0',\
setvar:tx.early_blocking=1"
また、mod_securityのログは急速に増大します。適切なログローテーションを設定するようにしてください。以下の設定では、監査ログは14日間保存し、デバッグログは7日間保存するようになっています。
sudo vim /etc/logrotate.d/apache2-modsecurity
/var/log/apache2/modsec_audit.log {
daily
rotate 14
compress
delaycompress
notifempty
create 640 root root
sharedscripts
postrotate
/usr/sbin/apache2ctl graceful > /dev/null 2>&1 || true
endscript
}
/var/log/apache2/modsec_debug.log {
daily
rotate 7
compress
delaycompress
notifempty
create 640 root root
sharedscripts
postrotate
/usr/sbin/apache2ctl graceful > /dev/null 2>&1 || true
endscript
}
fail2banとの連携
fail2banを設定していきます。私の場合はCloudFlareなどのCDNサービスのIPアドレスをignoreipに追加しています。CloudFlare経由でサイトにアクセスしている場合、実際のクライアントIPではなくCloudFlareのIPアドレスがログに記録されます。もしCloudFlareのIPをBANしてしまうと、サービス全体にアクセスできなくなってしまうからです。
※Cloudflareで実IPを取得する方法については以下の記事を参考にしてみてください。
今回の設定では、10分間に2回の攻撃を検知した場合、そのIPアドレスを7日間ブロックします。maxretryを2に設定しているのは、ModSecurityが攻撃を検知した時点で、それは明らかに悪意のあるアクセスだと判断できるためです。bantimeを7日間と長めに設定しているのは、自動化された攻撃ツールが繰り返しアクセスを試みることを防ぐためです。
sudo vim /etc/fail2ban/jail.d/apache-modsecurity.conf
[apache-modsecurity]
enabled = true
ignoreip = 127.0.0.1/8
::1
162.158.0.0/15
173.245.48.0/20
103.21.244.0/22
103.22.200.0/22
103.31.4.0/22
141.101.64.0/18
108.162.192.0/18
190.93.240.0/20
188.114.96.0/20
197.234.240.0/22
198.41.128.0/17
port = http,https
filter = apache-modsecurity
logpath = /var/log/apache2/error.log
maxretry = 2
bantime = 7d
findtime = 10m
設定を保存しましたら、fail2banを再起動します
sudo fail2ban-client -t
# 問題なければ
sudo systemctl restart fail2ban
ログ確認
fail2banが正常に動作しているか確認します。fail2ban-clientコマンドを使用して、apache-modsecurity jailの状態を表示できます。
# jail状態の確認
sudo fail2ban-client status apache-modsecurity
Status for the jail: apache-modsecurity
|- Filter
| |- Currently failed: 0
| |- Total failed: 50
| `- File list: /var/log/apache2/modsec_audit.log
`- Actions
|- Currently banned: 30
|- Total banned: 31
`- Banned IP list: 106.54.124.78 136.144.35.160 138.197.167.75 138.68.86.32 142.93.107.190 154.8.198.199 159.223.193.66 192.34.63.233 195.178.110.201 195.178.110.242 204.76.203.8 209.97.137.68 35.216.183.140 45.133.74.43 45.148.10.154 45.148.10.158 45.148.10.63 47.251.13.59 49.248.192.204 74.208.7.160 78.153.140.128 78.153.140.178 78.153.140.179 78.153.140.195 78.153.140.203 95.214.52.169 96.41.38.202 194.180.49.174 74.7.242.23 134.199.168.23
この出力例では、すでに28個のIPアドレスがブロックされています。Currently bannedが現在ブロック中のIPアドレス数で、Total bannedがこれまでにブロックした累計数です。Banned IP listには、実際にブロックされているIPアドレスが表示されます。
実際の運用では、定期的にこのリストを確認して、どのようなIPアドレスから攻撃を受けているかを把握することが重要です。同じ国や同じASNからの攻撃が多い場合は、ファイアウォールレベルで広範囲にブロックすることも検討できます。
補足 特定ルールの誤検知
運用を開始すると、正規のリクエストが誤って攻撃と判定される可能性があります。特定のルールが誤検知を起こす場合は、除外ルールを作成して対応します。
除外ルールは、RESPONSE-999-EXCLUSION-RULES-AFTER-CRS.confファイルで設定します。このファイルは、すべてのCRSルールが評価された後に読み込まれるため、特定のルールを無効化したり、特定の条件下でルールをスキップしたりすることができます。
たとえば、ルールID 942100が誤検知を起こしている場合、以下のように除外設定を追加します。
※最初の例は、ルール942100を完全に無効化します。2番目の例は、/api/で始まるURLパスに対してのみルール942100を無効化します。3番目の例は、searchという名前のパラメータに対してのみルール942100を無効化します。
sudo vim /etc/apache2/mod_security2.d/rules/RESPONSE-999-EXCLUSION-RULES-AFTER-CRS.conf
# 1
SecRuleRemoveById 942100
# 2
SecRule REQUEST_URI "@beginsWith /api/" \
"id:2000,\
phase:1,\
pass,\
t:none,\
nolog,\
ctl:ruleRemoveById=942100"
# 3
SecRule ARGS:search "@rx .*" \
"id:2001,\
phase:1,\
pass,\
t:none,\
nolog,\
ctl:ruleRemoveById=942100"
WordPressなどで、正規のリクエストが誤検知されてしまう可能性があります。もし誤検知をされてしまう場合は以下のように除外ルールを作成してください
# WordPress管理画面の除外
SecRule REQUEST_URI "@beginsWith /wp-admin/" \
"id:1000,\
phase:1,\
pass,\
t:none,\
nolog,\
ctl:ruleRemoveById=920440,\
ctl:ruleRemoveById=942100,\
ctl:ruleRemoveById=942190,\
ctl:ruleRemoveById=942200,\
ctl:ruleRemoveById=942260,\
ctl:ruleRemoveById=942340,\
ctl:ruleRemoveById=942370"
# WordPress AJAX処理の除外
SecRule REQUEST_URI "@beginsWith /wp-admin/admin-ajax.php" \
"id:1001,\
phase:1,\
pass,\
t:none,\
nolog,\
ctl:ruleRemoveById=942100,\
ctl:ruleRemoveById=942200"
# WordPress REST APIの除外
SecRule REQUEST_URI "@beginsWith /wp-json/" \
"id:1002,\
phase:1,\
pass,\
t:none,\
nolog,\
ctl:ruleRemoveById=920300,\
ctl:ruleRemoveById=942100"
まとめ
OWASP CRSを活用することで、独自にルールを作成する労力をかけずに、世界標準のセキュリティ対策を導入できます。継続的にアップデートされるルールセットにより、新しい脅威にも対応できます。
誰かの参考になれば幸いです。
運用時は、最初は設定を変更せずデフォルト値で運用を開始し、ログを観察しながら必要に応じてParanoia Levelを調整していくことをお勧めします。誤検知が発生した場合は、除外ルールを設定して対応してください。
以上、mod_security2+OWASP+fail2banのやり方についてでした。
おわり
参考:
Modsecurity
https://github.com/owasp-modsecurity/ModSecurity/wiki/Reference-Manual-(v2.x)
OWASP CRS
https://coreruleset.org/
https://github.com/coreruleset/coreruleset
https://coreruleset.org/docs/1-getting-started/1-1-crs-installation/
fail2ban
https://github.com/fail2ban/fail2ban
https://fail2ban.readthedocs.io/en/latest/
OpenSUSE
https://software.opensuse.org/package/apache2-mod_security2