ラクス Advent Calendar 2016の9日目です。
昨日は@nyakomeさんの「Selenideでテストコードを書いてみた」でした。
何を書こうか迷ったのですが、WAF(Web Application Firewall)について書いてみようと思います。個人的に興味がありつつも漠然とした知識しかなかったので、この機会に調べてみました。
WAFにも色々あるようですが、オープンソースのModSecurityというWAFがあるので、今回はこれを選択しました。
#WAFの概要
WAF(わふ)は「Web Application Firewall」の略で、その名の通り、Webアプリケーションを悪意のある通信から守ることを目的としています。
万が一、Webアプリケーションに脆弱性があった場合に、SQLインジェクションやクロスサイトスクリプティングなどの攻撃から守ってくれたりします。もちろん絶対ではないですが。
Webアプリケーションを改修することなく設置できる反面、守る対象のWebアプリケーションに特化した作りではないため、正常な通信を悪意のある通信と誤検知する可能性もあります。実運用の前に充分なテストが不可欠です。
#ModSecurityのインストールと動作確認
ModSecurityのインストールと動作について簡単に触れておきます。
動作環境としてCentOS7.2を用意。Apacheのモジュールとして動作させてみます。Apacheはセットアップ済みの状態です。
ModSecurityをインストールするだけだと最低限のルールしか設定されていませんので、CRSと呼ばれるルールセットも一緒にインストールします。
yum -y install mod_security mod_security_crs
私の環境では下記のバージョンがインストールされました。
- ModSecurity 2.7.3
- CRS 2.2.6
Apacheを再起動した後、怪しげにアクセスしてみます・・・
おおお!しっかりブロックされました。
ちゃんと動いているようです。
検知ログもちゃんと出てますね。SQLインジェクションのルールに先に当たってしまったようですが。。
#ModSecurityのルール設定
サクサクっとCRSを入れてしまいましたが、どんなルールが入ってるのでしょうか。
分からないと正しいリクエストもブロックされてしまいそうで、安心して使えませんよね。
CRSではたくさんのルールが設定されています。
すべてを解説できると良いのですが、あまりに多いので、ルール設定の読み方などを簡単にご紹介したいと思います。
##phase
まずphase
について説明します。
ModSecurityによる遮断などの処理を実行できるタイミングは、リクエスト処理中のある1点だけではありません。次の5つのタイミングのいずれかで実行することができます。
1
: Phase リクエストヘッダー
2
: Phase リクエストボディ
3
: Phase レスポンスヘッダー
4
: Phase レスポンスボディ
5
: Phase ロギング
これらはphase
と呼ばれており、それぞれ番号が付いています。
リクエストの内容次第で通信を遮断したければ、1
や2
のphase
を指定して実行させます。
またリクエストだけではなく、レスポンスの内容に特定の文字が入っていたら通信を遮断する、といったことがしたければ、3
や4
のphase
で実行させることもできるようです。
5
のロギングのphase
は他と違って、通信を遮断したりすることはできないようですね。
すでにレスポンスを返した後のタイミングでの実行となります。
詳しくは本家のProcessing_Phasesの図を見ると掴みやすいかも知れません。
##設定ディレクティブ
ルールの設定をするディレクティブを3つだけ紹介します。
###SecRule
概要: 遮断などの処理を実行するルールを設定します。
構文: SecRule VARIABLES OPERATOR [ACTIONS]
VARIABLES
: ModSecurityで扱える変数を指定
OPERATOR
: VARIABLES
に対する検査方法を指定。
@
の後にOPERATOR
を書きます。
@
OPERATOR
を省略すると、@rx
(正規表現マッチ)とみなされます。
ACTIONS
: VARIABLES
とOPERATOR
の条件にマッチした場合に実行する処理を指定
ACTIONS
を省略すると、後述のSecDefaultAction
が効きます。
###SecAction
概要: 無条件で処理を実行します。変数の初期化などに使うようです。
構文: SecAction ACTIONS
ACTIONS
: 実行する処理を指定
###SecDefaultAction
概要: 検知パターンにマッチした時のデフォルトのアクションを定義します。
構文: SecDefaultAction ACTIONS
ACTIONS
: 実行する処理を指定
###おまけ:SecRuleEngine
概要: 動作モードを指定します。
構文: SecRuleEngine On|Off|DetectionOnly
On
やOff
は言わずもがなですね。
DetectionOnly
は検知だけのモードです。
実態としてはDisruptive actions
に分類されるACTIONS
(block, deny, drop, allow, proxy and redirect)
がすべて動作しないモードのようですので、allowなどは注意が要るかも知れません。
知らないと「allowで検査が止まらない!なんでだ!」みたいな混乱をしそうです。
DetectionOnly
は検知ログを出力しつつブロックしないので、
誤検知されないか検証環境のWebアプリケーションで確認する際に重宝しそうです。
誤検知確認の時にいちいちブロックされていたら、
スムーズに検証できないケースもあるかと思いますので。
##CRSで定義されているルール
では実際にCRSに入っているルールを見てみます。
###CRSのSecDefaultAction
SecDefaultAction "phase:1,deny,log"
SecDefaultAction
として下記のAction
が定義されています。
phase:1
:
1
(=リクエストヘッダー)のphase
で実行する
deny
:
ルールの処理を停止し、トランザクションをインターセプトする
log
:
ApacheのerrorログとModSecurityのAuditログに出力する
###CRSのSecActionの一例
SecAction \
"id:'900001', \
phase:1, \
t:none, \
setvar:tx.critical_anomaly_score=5, \
setvar:tx.error_anomaly_score=4, \
setvar:tx.warning_anomaly_score=3, \
setvar:tx.notice_anomaly_score=2, \
nolog, \
pass"
ここでは定数の定義にSecAction
を利用しています。深刻度のスコア計算に使う値をセットしているようですね。
id:'900001'
:
ルールのID(900000~999999はCRSのために予約されています)
phase:1
:
1
(=リクエストヘッダー)のphase
で実行する
t:none
:
t
は変換関数の実行を表します。
none
はそれまでに掛かっている変換関数の効果を消す関数です。
SecDefaultAction
から変換関数の影響を受けている可能性があるので,
依存しないように初めにこれを書くのが作法らしいのですが、
このACTION構成のSecAction
で実行する効果はよく分かりませんでした(汗)すみません。
setvar:tx.critical_anomaly_score=5
:
setvar
は変数の操作です。
tx.critical_anomaly_score
という変数を作成し、5
を代入しています。
後で閾値にでも使うのですかね。
setvar:tx.error_anomaly_score=4
:
上とほぼ同じ。
setvar:tx.warning_anomaly_score=3
:
上とほぼ同じ。
setvar:tx.notice_anomaly_score=2
:
上とほぼ同じ。
nolog
:
ApacheのerrorログにもModSecurityのAuditログにも出力しません。
pass
:
次のルール検証に進みます。
これがないとSecDefaultAction
でdeny
が指定されているため、ルール検証が止まります。
###CRSのSecRuleの一例
SecRule \
REQUEST_COOKIES|!REQUEST_COOKIES:/__utm/|REQUEST_COOKIES_NAMES|ARGS_NAMES|ARGS|XML:/* \
"(/\*!?|\*/|[';]--|--[\s\r\n\v\f]|(?:--[^-]*?-)|([^\-&])#.*?[\s\r\n\v\f]|;?\\x00)" \
"phase:2, \
rev:'2', \
ver:'OWASP_CRS/2.2.6', \
maturity:'8', \
accuracy:'8', \
id:'981231', \
t:none, \
t:urlDecodeUni, \
block, \
msg:'SQL Comment Sequence Detected.', \
severity:'2', \
capture, \
logdata:'Matched Data: %{TX.0} found within %{MATCHED_VAR_NAME}: %{MATCHED_VAR}', \
tag:'OWASP_CRS/WEB_ATTACK/SQL_INJECTION', \
tag:'WASCTC/WASC-19', \
tag:'OWASP_TOP_10/A1', \
tag:'OWASP_AppSensor/CIE1', \
tag:'PCI/6.5.2', \
setvar:tx.anomaly_score=+%{tx.critical_anomaly_score}, \
setvar:tx.sql_injection_score=+1, \
setvar:'tx.msg=%{rule.msg}', \
setvar:tx.%{rule.id}-OWASP_CRS/WEB_ATTACK/SQL_INJECTION-%{matched_var_name}=%{tx.0}"
SQLインジェクション攻撃の一種にSQLコメントを含むものがありますが、それを検知しようというルールです。
構文(SecRule VARIABLES OPERATOR ACTIONS
)と照らし合わせてみますと、
REQUEST_COOKIES|!...
の行がVARIABLES
、
"(/\*!?|...
の行がOPERATOR
です。
@
OPERATOR
が無いので正規表現マッチですね。
"phase:2, \...
以降がACTIONS
です。
詳しくは本家サイトにお任せするとして
setvar:tx.anomaly_score=+%{tx.critical_anomaly_score}, \
の箇所で、上述のSecAction
で定義されたtx.critical_anomaly_score
がスコアのカウントアップに使われているのが分かります。
#まとめ
ModSecurityのインストールと、ルールの構文について、簡単にご紹介しました。とりあえずこれくらいの知識があれば、構文を本家サイトで調べつつ読み進めて行けるのではないかと思います。
調べるとキリがないくらい機能が盛り沢山ですが、これから導入を検討されている方に、最初の一歩として少しでも参考になれば幸いです。
今回はWAF導入に当たっての検証の必要性にはあまり触れませんでしたが、まずは検証環境で、どれくらい負荷が掛かるのか、誤検知がないかなどを確認されることを最後にお勧めしておきます。
#参考サイト