mod_security#
last update 2024/03/04
summary#
GoAccess is an open source real-time web log analyzer.
前提条件#
OS |
Apache |
|---|---|
AlmaLinux 9 |
2.4.57 |
Install#
dnf -y install mod_security mod_security_crs
module |
Feature |
|---|---|
mod_security |
WAF |
mod_security_crs |
ルールセット |
コンフィグ設定#
機能の有効・無効#
Property |
Feature |
Value |
|---|---|---|
SecRuleEngine |
WAFの有効無効 |
On=有効 |
Off=無効 |
||
SecRequestBodyAccess |
リクエスト本文へのアクセスの有効無効 |
On=リクエスト本文のバッファリングを有効 |
Off=リクエスト本文のバッファリングを無効 |
検知ルール#
選択した演算子を使用して選択した変数を分析するルールを作成する
構文#
SecRule#
注釈
SecRule VARIABLES OPERATOR [ACTIONS]
SecRule ARGS "@rx attack" "phase:1,log,deny,id:1"
Parameter |
Notes |
|---|---|
VARIABLES |
下記変数を指定 |
OPERATOR |
@<OPERATOR> と書く OPERATORを省略すると正規表現マッチ(@rx)と見なされる |
ACTIONS |
条件にマッチした場合に実行する処理 省略した場合は**SecDefaultAction**が適用される |
SecAction#
注釈
SecAction "action1,action2,action3"
SecAction nolog,phase:1,initcol:RESOURCE=%{REQUEST_FILENAME}
Parameter |
Notes |
|---|---|
ACTIONS |
アクション1,アクション2... |
SecDefaultAction#
注釈
SecDefaultAction "action1,action2,action3"
SecDataDir#
注釈
SecDataDir /path/to/dir
SecDataDir /usr/local/apache/logs/data
SecResponseBodyAccess#
注釈
SecResponseBodyAccess On|Off
SecResponseBodyMimeType#
注釈
SecResponseBodyMimeType MIMETYPE MIMETYPE ...
# example
SecResponseBodyMimeType text/plain text/html text/xml
# default
text/plain text/html
変数 (VARIABLES)#
リクエスト#
Variable |
Collection |
|---|---|
REQUEST_COOKIES |
リクエストクッキー(値のみ) |
REQUEST_COOKIES_NAMES |
リクエストクッキーの名前 |
REQUEST_FILENAME |
クエリ文字列部分を除いた相対リクエストURLを保持 |
REQUEST_METHOD |
トランザクションで仕様されるリクエストメソッドを保持 |
REQUEST_PROTOCOL |
リクエストプロトコルバージョン情報を保持 |
REQUEST_LINE |
リクエストメソッド・HTTPバージョン情報を含む行全体を保持 |
# REQUEST_COOKIES (Cookieヘッダが含まれていない場合にトリガされる例)
SecRule &REQUEST_COOKIES "@eq 0" "id:44"
# REQUEST_COOKIES_NAMES (JSESSIONIDクッキーが存在しない場合にトリガされる例)
SecRule &REQUEST_COOKIES_NAMES:JSESSIONID "@eq 0" "id:45"
# REQUEST_FILENAME
SecRule REQUEST_FILENAME "^/cgi-bin/login.php$" phase:2,id:46,t:none,t:normalizePath
# REQUEST_METHOD
SecRule REQUEST_METHOD "^(?:CONNECT|TRACE)$" "id:50,t:none"
# REQUEST_PROTOCOL
SecRule REQUEST_PROTOCOL "!^HTTP/(0.9|1.0|1.1)$" "id:51"
# REQUEST_LINE (POST/GET/HEADリクエストのみを許可する例)
SecRule REQUEST_LINE "!(^((:(:POS|GE)T|HEAD))|HTTP/(0.9|1.0|1.1)$)" "phase:1,id:49,log,block,t:none"
レスポンス#
Variable |
Collection |
|---|---|
RESPONSE_STATUS |
HTTP応答コードを保持 |
RESPONSE_BODY |
レスポンスボディのデータを保持(バッファリングが有効になっている場合のみ) |
# RESPONSE_STATUS (4xx・5xxを拒否する例)
SecRule RESPONSE_STATUS "^[45]" "phase:3,id:58,t:none"
# RESPONSE_BODY
SecRule RESPONSE_BODY "ODBC Error Code" "phase:4,id:54,t:none"
リモート#
Variable |
Collection |
|---|---|
REMOTE_ADDR |
クライアントのIPアドレスを保持 |
REMOTE_HOST |
名前解決したリモートホスト名を保持(ApacheのHostnameLookupsがONの場合) |
REMOTE_PORT |
接続に使用したソースポートを保持 |
REMOTE_USER |
認証ユーザ名を保持(Basic認証・Digest認証) |
# REMOTE_ADDR
SecRule REMOTE_ADDR "@ipMatch 192.168.1.101" "id:35"
# REMOTE_HOST
SecRule REMOTE_HOST "\.evil.networkorg$" "id:36"
# REMOTE_PORT (1024より小さいかどうかを評価する例)
SecRule REMOTE_PORT "@lt 1024" "id:37"
# REMOTE_USER
SecRule REMOTE_USER "@streq admin" "id:38"
コレクション#
Collection |
Description |
|---|---|
ARGS |
静的パラメタ、正規表現を含む引数を指定 |
ARGS_GET |
クエリ文字列パラメタのみ対象 |
ARGS_POST |
POSTボディの引数のみ対象 |
ARGS_NAMES |
すべてのリクエストパラメタ名を含む(感嘆符のついた反転ルールを使って引数を許可することも可能) |
# ARGS (すべてのリクエスト引数を調べる例)
SecRule ARGS dirty "id:7"
# ARGS ([p] という名前の引数を指定する例 ARGS:<指定引数>)
SecRule ARGS:p dirty "id:8"
# ARGS ([p] という名前の引数を除外する例 ARGS|!ARGS:<指定引数>)
SecRule ARGS|!ARGS:p dirty "id:9"
# ARGS (引数の数をカウントする例=0以上の場合 $ARGS !^<数>$)
SecRule &ARGS !^0$ "id:10"
# ARGS ([id_] で始まる名前をもつすべての引数を指定する例(ARGS:/<引数>/)
SecRule ARGS:/^id_/ dirty "id:11"
# ARGS_NAMES ([p][a]の引数名を許可する例)
SecRule ARGS_NAMES "!^(p|a)$" "id:13"
id#
注釈
id ranges |
Reserved |
available |
|---|---|---|
1–99999 |
local use |
|
100000–399999 |
used |
|
400000–419999 |
unused |
○ |
420000–439999 |
used |
|
440000-599999 |
unused |
○ |
600000-4300999 |
used |
|
4301000-19999999 |
unused |
○ |
20000000-21999999 |
used |
|
22000000-69999999 |
unused |
○ |
77000000-99209999 |
used |
t:none#
注釈
Action#
phase#
Command |
Phase |
Description |
|---|---|---|
phase:1 |
リクエストヘッダ |
リクエストヘッダを読み終わった直後に処理(ボディを読込む前) |
phase:2 |
リクエストボディ |
汎用入力分析フェーズ リクエストボディフェースのデータにアクセスするために SecRequestBodyAccessをOnに設定 |
phase:3 |
レスポンスヘッダ |
レスポンスヘッダがクライアントに返される直前に処理 |
phase:4 |
レスポンスボディ |
汎用出力分析フェーズ 送信されたHTMLの情報開示、エラーメッセージ、認証に失敗したテキストなどを検査 SecRequestBodyAccessをOnに設定 |
phase:5 |
ロギング |
ロギングが実行される直前に実行 ログ収集されたエラーメッセージを検査 このフェーズでは接続を拒否・ブロックすることができない |
accuracy (アクショングループ:メタデータ)#
注釈
allow (アクショングループ:破壊的)#
注釈
# 192.168.1.100 からのアクセスを許可
SecRule REMOTE_ADDR "^192.168.1.100$" phase:1,id:95,nolog,allow
pass (アクショングループ:破壊的)#
注釈
SecRule REQUEST_HEADERS:User-Agent "Test" "log,pass,id:122"
block (アクショングループ:破壊的)#
注釈
# どのようにブロックするかを指定
SecDefaultAction phase:2,deny,id:101,status:403,log,auditlog
# ブロックする
SecRule ARGS attack1 phase:2,block,id:102
drop (アクショングループ:破壊的)#
注釈
# Basic認証の試行を追跡=IP収拾の開始、クライアントが2分間に25回以上の試行を超えた場合dropする
SecAction phase:1,id:109,initcol:ip=%{REMOTE_ADDR},nolog
SecRule ARGS:login "!^$" "nolog,phase:1,id:110,setvar:ip.auth_attempt=+1,deprecatevar:ip.auth_attempt=25/120"
SecRule IP:AUTH_ATTEMPT "@gt 25" "log,drop,phase:1,id:111,msg:'Possible Brute Force Attack'"
append (アクショングループ:非破壊)#
注釈
SecRule RESPONSE_CONTENT_TYPE "^text/html" "nolog,id:99,pass,append:'<hr>フッター'"
auditlog (アクショングループ:非破壊)#
注釈
SecRule REMOTE_ADDR "^192\.168\.1\.100$" auditlog,phase:1,id:100,allow
log (アクショングループ:非破壊)#
注釈
SecAction phase:1,id:117,pass,initcol:ip=%{REMOTE_ADDR},log
nolog (アクショングループ:非破壊)#
注釈
SecRule REQUEST_HEADERS:User-Agent "Test" allow,nolog,id:121
intcol (アクショングループ:非破壊)#
注釈
# IPアドレス追跡を開始
SecAction phase:1,id:116,nolog,pass,initcol:ip=%{REMOTE_ADDR}
設定パタン#
ホワイトリスト (IPアドレス)#
SecRule REMOTE_ADDR "@pmFromFile modsec-white-ip" "phase:1,id:400001,nolog,allow,ctl:ruleEngine=Off,ctl:auditEngine=Off"
112.34.56.78
222.34.56.78
332.34.56.78
ブラックリスト (IPアドレス)#
SecRule REMOTE_ADDR "@pmFromFile modsec-black-ip" "phase:1,id:400002,drop,msg:'Blacklisted IP address'"
112.34.56.78
222.34.56.78
332.34.56.78
ブラックリスト (URL文字列)#
SecRule REQUEST_URI "@pmFromFile modsec-black-uri" "phase:1,id:400003,drop,auditlog"
1<拒否する文字列>
2<拒否する文字列>
DOS対策・ブルーとフォース対策#
# @streq 401 = HTTP応答コード401を検知
# setvar: = 変数を作成・削除・更新する (大文字・小文字を区別しない) ここでは ip.401_cnt に+1を加算
# deprecatevar: = 360秒(6分)毎に1減算する 360秒以内に1回以上のアクセスがあるかを検出
# expirevar: = 有効期限を秒数で指定 expirevar:ip.401_cnt=86400 などとする
# ip:401_cnt "@gt 1" = 変数が1以上の場合、リクエストボディ取得時にパケットを即時終了する
SecAction phase:1,id:410001,nolog,initcol:ip=%{REMOTE_ADDR}
SecRule RESPONSE_STATUS "@streq 401" "phase:5,id:410002,t:none,nolog,pass,setvar:ip.401_cnt=+1,deprecatevar:ip.401_cnt=1/360"
SecRule ip:401_cnt "@gt 1" "phase:2,id:410003,drop,auditlog,msg:'upper limit of 401 has been exceeded'"
# basic認証・digest認証のブルートフォースを検知
#
SecAction phase:1,id:410004,nolog,initcol:ip=%{REMOTE_ADDR}
SecRule ARGS:login "!^$" "nolog,phase:1,id:410005,setvar:ip.auth_attempt=+1,deprecatevar:ip.auth_attempt=25/120"
SecRule IP:AUTH_ATTEMPT "@gt 25" "auditlog,drop,phase:1,id:410006,msg:'Possible Brute Force Attack'"
監査ログ#
セパレータフォーマット#
--<ID>-<分類>--
分類#
文字 |
説明 |
|---|---|
A |
監査ログヘッダ(必須) |
B |
リクエストヘッダ |
C |
リクエストボディ (リクエストボディが存在し、ModSecurityがそれを傍受するように 設定されている場合にのみ存在する。これにはSecRequestBodyAccessをonに設定する必要がある) |
D |
中間応答ヘッダーのために予約 |
E |
中間レスポンスボディ(ModSecurity がレスポンスボディを傍受するように設定されており、監査ログエンジンがそれを記録するように設定されている場合にのみ存在する。レスポンスボディを傍受するには、SecResponseBodyAccess が有効になっている必要がある)。ModSecurity が中間レスポンスボディを傍受しない限り、中間レスポンスボディは実際のレスポンスボディと同じ |
F |
最終的なレスポンスヘッダ (Date と Server ヘッダを除く。これは Apache が常にコンテンツ配送の後期に追加) |
G |
実際のレスポンスボディのために予約 |
H |
監査ログのトレイラー |
I |
この部分は ModSecurity v3 では未実装 |
J |
このパートは、multipart/form-data エンコーディングを使ってアップロードされたファイルに関する情報を含む |
K |
この部分は ModSecurity v3 では未実装 |
Z |
最終境界、エントリーの終わりを意味する(必須) |