ModSecurityのルール設定を読めるようになりたい！

[ラクス Advent Calendar 2016](http://qiita.com/advent-calendar/2016/rakus)の9日目です。

昨日は@nyakomeさんの「[Selenideでテストコードを書いてみた](http://qiita.com/nyakome/items/207daf3050809c269e8e)」でした。

何を書こうか迷ったのですが、WAF（Web Application Firewall）について書いてみようと思います。個人的に興味がありつつも漠然とした知識しかなかったので、この機会に調べてみました。

WAFにも色々あるようですが、オープンソースの[ModSecurity](https://modsecurity.org/)というWAFがあるので、今回はこれを選択しました。

#WAFの概要

WAF（わふ）は「Web Application Firewall」の略で、その名の通り、Webアプリケーションを悪意のある通信から守ることを目的としています。

万が一、Webアプリケーションに脆弱性があった場合に、SQLインジェクションやクロスサイトスクリプティングなどの攻撃から守ってくれたりします。もちろん絶対ではないですが。

Webアプリケーションを改修することなく設置できる反面、守る対象のWebアプリケーションに特化した作りではないため、正常な通信を悪意のある通信と誤検知する可能性もあります。実運用の前に充分なテストが不可欠です。

#ModSecurityのインストールと動作確認

ModSecurityのインストールと動作について簡単に触れておきます。

動作環境としてCentOS7.2を用意。Apacheのモジュールとして動作させてみます。Apacheはセットアップ済みの状態です。

ModSecurityをインストールするだけだと最低限のルールしか設定されていませんので、[CRS](http://www.owasp.org/index.php/Category:OWASP_ModSecurity_Core_Rule_Set_Project)と呼ばれるルールセットも一緒にインストールします。

```
yum -y install mod_security mod_security_crs
```

私の環境では下記のバージョンがインストールされました。

* ModSecurity 2.7.3
* CRS 2.2.6

Apacheを再起動した後、怪しげにアクセスしてみます・・・
![スクリーンショット 2016-12-05 22.10.49.png](https://qiita-image-store.s3.amazonaws.com/0/151018/2c85c923-4dc2-32c0-6132-6047f5e52853.png)
おおお！しっかりブロックされました。
ちゃんと動いているようです。

検知ログもちゃんと出てますね。SQLインジェクションのルールに先に当たってしまったようですが。。
![スクリーンショット 2016-12-08 23.41.08_2.png](https://qiita-image-store.s3.amazonaws.com/0/151018/f4ee68ba-3dde-46a9-8a34-c591d1770fe2.png)




#ModSecurityのルール設定

サクサクっと[CRS](http://www.owasp.org/index.php/Category:OWASP_ModSecurity_Core_Rule_Set_Project)を入れてしまいましたが、どんなルールが入ってるのでしょうか。
分からないと正しいリクエストもブロックされてしまいそうで、安心して使えませんよね。

[CRS](http://www.owasp.org/index.php/Category:OWASP_ModSecurity_Core_Rule_Set_Project)ではたくさんのルールが設定されています。
すべてを解説できると良いのですが、あまりに多いので、ルール設定の読み方などを簡単にご紹介したいと思います。

##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](https://github.com/SpiderLabs/ModSecurity/wiki/Reference-Manual#Processing_Phases)の図を見ると掴みやすいかも知れません。

##設定ディレクティブ
ルールの設定をするディレクティブを3つだけ紹介します。

###**SecRule**
**概要**：　遮断などの処理を実行するルールを設定します。
**構文**：　`SecRule VARIABLES OPERATOR [ACTIONS]`
　[**`VARIABLES`**](https://github.com/SpiderLabs/ModSecurity/wiki/Reference-Manual#Variables)：　ModSecurityで扱える変数を指定
　[**`OPERATOR`**](https://github.com/SpiderLabs/ModSecurity/wiki/Reference-Manual#Operators)：　[`VARIABLES`](https://github.com/SpiderLabs/ModSecurity/wiki/Reference-Manual#Variables)に対する検査方法を指定。
　　`@`の後に[`OPERATOR`](https://github.com/SpiderLabs/ModSecurity/wiki/Reference-Manual#Operators)を書きます。
　　`@`[`OPERATOR`](https://github.com/SpiderLabs/ModSecurity/wiki/Reference-Manual#Operators)を省略すると、[`@rx`](https://github.com/SpiderLabs/ModSecurity/wiki/Reference-Manual#rx)（正規表現マッチ）とみなされます。
　[**`ACTIONS`**](https://github.com/SpiderLabs/ModSecurity/wiki/Reference-Manual#Actions)：　[`VARIABLES`](https://github.com/SpiderLabs/ModSecurity/wiki/Reference-Manual#Variables)と[`OPERATOR`](https://github.com/SpiderLabs/ModSecurity/wiki/Reference-Manual#Operators)の条件にマッチした場合に実行する処理を指定
[　`ACTIONS`](https://github.com/SpiderLabs/ModSecurity/wiki/Reference-Manual#Actions)を省略すると、後述の`SecDefaultAction`が効きます。

###**SecAction**
**概要**：　無条件で処理を実行します。変数の初期化などに使うようです。
**構文**：　`SecAction ACTIONS`
　[**`ACTIONS`**](https://github.com/SpiderLabs/ModSecurity/wiki/Reference-Manual#Actions)：　実行する処理を指定

###**SecDefaultAction**
**概要**：　検知パターンにマッチした時のデフォルトのアクションを定義します。
**構文**：　`SecDefaultAction ACTIONS`
　[**`ACTIONS`**](https://github.com/SpiderLabs/ModSecurity/wiki/Reference-Manual#Actions)：　実行する処理を指定

###おまけ：**SecRuleEngine**
**概要**：　動作モードを指定します。
**構文**：　`SecRuleEngine On|Off|DetectionOnly`
　`On`や`Off`は言わずもがなですね。
　`DetectionOnly`は検知だけのモードです。
　実態としては`Disruptive actions`に分類される[`ACTIONS`](https://github.com/SpiderLabs/ModSecurity/wiki/Reference-Manual#Actions)
　（block, deny, drop, allow, proxy and redirect）
　がすべて動作しないモードのようですので、allowなどは注意が要るかも知れません。
　知らないと「allowで検査が止まらない！なんでだ！」みたいな混乱をしそうです。

　`DetectionOnly`は検知ログを出力しつつブロックしないので、
　誤検知されないか検証環境のWebアプリケーションで確認する際に重宝しそうです。
　誤検知確認の時にいちいちブロックされていたら、
　スムーズに検証できないケースもあるかと思いますので。


##CRSで定義されているルール

では実際に[CRS](http://www.owasp.org/index.php/Category:OWASP_ModSecurity_Core_Rule_Set_Project)に入っているルールを見てみます。

###CRSのSecDefaultAction

```modsecurity_crs_10_config.conf
SecDefaultAction "phase:1,deny,log"
```

`SecDefaultAction`として下記の`Action`が定義されています。

**`phase:1`**：
　`1`（＝リクエストヘッダー）の`phase`で実行する
**`deny`**：
　ルールの処理を停止し、トランザクションをインターセプトする
**`log`**：
　ApacheのerrorログとModSecurityのAuditログに出力する

###CRSのSecActionの一例

```modsecurity_crs_10_config.conf
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](http://www.owasp.org/index.php/Category:OWASP_ModSecurity_Core_Rule_Set_Project)のために予約されています）
**`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の一例

```activated_rules/modsecurity_crs_41_sql_injection_attacks.conf
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`](https://github.com/SpiderLabs/ModSecurity/wiki/Reference-Manual#Variables)、

`"(/\*!?|...`の行が[`OPERATOR`](https://github.com/SpiderLabs/ModSecurity/wiki/Reference-Manual#Operators)です。
`@`[`OPERATOR`](https://github.com/SpiderLabs/ModSecurity/wiki/Reference-Manual#Operators)が無いので正規表現マッチですね。

`"phase:2, \...`以降が[`ACTIONS`](https://github.com/SpiderLabs/ModSecurity/wiki/Reference-Manual#Actions)です。
詳しくは本家サイトにお任せするとして :sweat_smile: 
`setvar:tx.anomaly_score=+%{tx.critical_anomaly_score}, \`の箇所で、上述の`SecAction`で定義された`tx.critical_anomaly_score`がスコアのカウントアップに使われているのが分かります。

#まとめ
ModSecurityのインストールと、ルールの構文について、簡単にご紹介しました。とりあえずこれくらいの知識があれば、構文を本家サイトで調べつつ読み進めて行けるのではないかと思います。

調べるとキリがないくらい機能が盛り沢山ですが、これから導入を検討されている方に、最初の一歩として少しでも参考になれば幸いです。

今回はWAF導入に当たっての検証の必要性にはあまり触れませんでしたが、まずは検証環境で、どれくらい負荷が掛かるのか、誤検知がないかなどを確認されることを最後にお勧めしておきます。

#参考サイト
* [ModSecurity/Reference Manual](https://github.com/SpiderLabs/ModSecurity/wiki/Reference-Manual)
* [IPA/Web Application Firewall 読本](https://www.ipa.go.jp/security/vuln/waf.html)