オープンソースのWAF「ModSecurity」をCentOSに構築する。

· 6 min read
オープンソースのWAF「ModSecurity」をCentOSに構築する。

ModSecurity はオープンソースで開発されている
WAF(Web Application Firewall)で、無償で利用することができます。

WAF とは、Web アプリケーションにリクエストが送信される手前でリクエストを取得して、
内容を精査し、問題があればリクエストを拒否します。
これにより仮に Web アプリケーションに脆弱性があったとしても WAF が守ってくれます。

WAF にも偽陽性や偽陰性(正当なリクエストを攻撃判定したり、逆に攻撃を見逃したりする事)の問題があるので、過信することはよくないですが、
改修不能な Web アプリケーションの保護や、多層防御の一手法として有効です。

また、WAF にはネットワーク型とホスト型がありますが、
ModSecurity はホスト型の WAF になります。


ModSecurity のインストール

構築する環境は以下の通り。
また、脆弱性テスト用に Apache をインストール済み。
CMS の Drupal を攻撃対象のページとします。

# cat /etc/redhat-release
CentOS release 6.8 (Final)

# arch
x86_64

# httpd -V
Server version: Apache/2.2.15 (Unix)

ModSecurity をインストールするために epel リポジトリを追加します。

# yum install epel-release

mod_security 関連をまとめてインストールします。

  • mod_security(ModSecurity 本体)
  • mod_security_crs (Core Rule Set)
  • mod_security_crs-extras (追加の Core Rule Set)

Core Rule Set は、OWASP が開発した、Web サイトに対する攻撃を検知するためのルールを定義したものです。

# yum install mod_security mod_security_crs mod_security_crs-extras
# rpm -qa mod_security mod_security_crs mod_security_crs-extras
mod_security_crs-2.2.6-3.el6.noarch
mod_security-2.7.3-3.el6.x86_64
mod_security_crs-extras-2.2.6-3.el6.noarch

ModSecurity の設定

デフォルトの設定だとフィルタリングが有効の状態なので、
テストのため、一時的にログに吐くだけの設定にします。

# cp -p /etc/httpd/conf.d/mod_security.conf /etc/httpd/conf.d/mod_security.conf.org  # バックアップ
# vi /etc/httpd/conf.d/mod_security.conf

# Default recommended configuration
# SecRuleEngine On          ← コメントアウト
SecRuleEngine DetectionOnly  ← 追記(攻撃検知時に遮断せずログ出力のみ)

Apache を再起動します。

# service httpd restart

これで ModSecurity が有効になりました。


ModSecurity が動くかテスト実施

下記のようなパラメータを付与してアクセスしてみます。

http://[ホストIP]/?union+select

今のままだとフィルタリングが有効になっていないので特段変わりなしですが、
実際にログが出力されているか確認してみます。
※デフォルトの定義では /var/log/httpd/modsec_audit.log に出力されています。

# tail -f /var/log/httpd/modsec_audit.log

--77c22b4e-A--
[19/Apr/2017:16:55:29 +0900] WPcX8QoAAAYAAAUyDsMAAAAH 192.168.1.3 54452 10.0.0.6 80
--77c22b4e-B--
GET /?union+select HTTP/1.1
Host: 10.0.0.6
Connection: keep-alive
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Encoding: gzip, deflate, sdch
Accept-Language: ja-JP,ja;q=0.8,en-US;q=0.6,en;q=0.4
Cookie: has_js=1

--77c22b4e-F--
HTTP/1.1 403 Forbidden
Accept-Ranges: bytes
Content-Length: 4961
Keep-Alive: timeout=15, max=100
Connection: Keep-Alive
Content-Type: text/html; charset=UTF-8

--77c22b4e-H--
Message: Warning. Pattern match "^[\\d.:]+$" at REQUEST_HEADERS:Host. [file "/etc/httpd/modsecurity.d/activated_rules/modsecurity_crs_21_protocol_anomalies.conf"] [line "98"] [id "960017"] [rev "2"] [msg "Host header is a numeric IP address"] [data "10.0.0.6"] [severity "WARNING"] [ver "OWASP_CRS/2.2.6"] [maturity "9"] [accuracy "9"] [tag "OWASP_CRS/PROTOCOL_VIOLATION/IP_HOST"] [tag "WASCTC/WASC-21"] [tag "OWASP_TOP_10/A7"] [tag "PCI/6.5.10"] [tag "http://technet.microsoft.com/en-us/magazine/2005.01.hackerbasher.aspx"]
Message: Warning. Pattern match "(?i:(?:(?:s(?:t(?:d(?:dev(_pop|_samp)?)?|r(?:_to_date|cmp))|u(?:b(?:str(?:ing(_index)?)?|(?:dat|tim)e)|m)|e(?:c(?:_to_time|ond)|ssion_user)|ys(?:tem_user|date)|ha(1|2)?|oundex|chema|ig?n|pace|qrt)|i(?:s(null|_(free_lock|ipv4_compat|ipv4_mapped|ipv4|ipv ..." at ARGS_NAMES:union select. [file "/etc/httpd/modsecurity.d/activated_rules/modsecurity_crs_41_sql_injection_attacks.conf"] [line "125"] [id "950001"] [rev "2"] [msg "SQL Injection Attack"] [data "Matched Data: union select found within ARGS_NAMES:union select: union select"] [severity "CRITICAL"] [ver "OWASP_CRS/2.2.6"] [maturity "9"] [accuracy "8"] [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"]
Message: Warning. Pattern match "(?i:(?:(?:s(?:t(?:d(?:dev(_pop|_samp)?)?|r(?:_to_date|cmp))|u(?:b(?:str(?:ing(_index)?)?|(?:dat|tim)e)|m)|e(?:c(?:_to_time|ond)|ssion_user)|ys(?:tem_user|date)|ha(1|2)?|oundex|chema|ig?n|pace|qrt)|i(?:s(null|_(free_lock|ipv4_compat|ipv4_mapped|ipv4|ipv ..." at ARGS_NAMES:union select. [file "/etc/httpd/modsecurity.d/activated_rules/modsecurity_crs_41_sql_injection_attacks.conf"] [line "143"] [id "959073"] [rev "2"] [msg "SQL Injection Attack"] [data "Matched Data: union select found within ARGS_NAMES:union select: union select"] [severity "CRITICAL"] [ver "OWASP_CRS/2.2.6"] [maturity "9"] [accuracy "8"] [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"]
Message: Warning. Pattern match "(?i:(?:\\sexec\\s+xp_cmdshell)|(?:[\"'`\xc2\xb4\xe2\x80\x99\xe2\x80\x98]\\s*?!\\s*?[\"'`\xc2\xb4\xe2\x80\x99\xe2\x80\x98\\w])|(?:from\\W+information_schema\\W)|(?:(?:(?:current_)?user|database|schema|connection_id)\\s*?\\([^\\)]*?)|(?:[\"'`\xc2\xb4\xe2 ..." at ARGS_NAMES:union select. [file "/etc/httpd/modsecurity.d/activated_rules/modsecurity_crs_41_sql_injection_attacks.conf"] [line "218"] [id "981255"] [msg "Detects MSSQL code execution and information gathering attempts"] [data "Matched Data: union select found within ARGS_NAMES:union select: union select"] [severity "CRITICAL"] [tag "OWASP_CRS/WEB_ATTACK/SQLI"]
Message: Warning. Pattern match "(?i:(?:union\\s*?(?:all|distinct|[(!@]*?)?\\s*?[([]*?\\s*?select)|(?:\\w+\\s+like\\s+[\"'`\xc2\xb4\xe2\x80\x99\xe2\x80\x98])|(?:like\\s*?[\"'`\xc2\xb4\xe2 ..." at ARGS_NAMES:union select. [file "/etc/httpd/modsecurity.d/activated_rules/modsecurity_crs_41_sql_injection_attacks.conf"] [line "234"] [id "981245"] [msg "Detects basic SQL authentication bypass attempts 2/3"] [data "Matched Data: union select found within ARGS_NAMES:union select: union select"] [severity "CRITICAL"] [tag "OWASP_CRS/WEB_ATTACK/SQLI"]
Message: Warning. Operator GE matched 5 at TX:inbound_anomaly_score. [file "/etc/httpd/modsecurity.d/activated_rules/modsecurity_crs_60_correlation.conf"] [line "37"] [id "981204"] [msg "Inbound Anomaly Score Exceeded (Total Inbound Score: 23, SQLi=14, XSS=): 981245-Detects basic SQL authentication bypass attempts 2/3"]
Apache-Error: [file "/builddir/build/BUILD/httpd-2.2.15/modules/generators/mod_autoindex.c"] [line 2292] [level 3] Directory index forbidden by Options directive: /var/www/html/
Stopwatch: 1492588529481677 7850 (- - -)
Stopwatch2: 1492588529481677 7850; combined=5096, p1=487, p2=4396, p3=0, p4=0, p5=211, sr=136, sw=2, l=0, gc=0
Producer: ModSecurity for Apache/2.7.3 (http://www.modsecurity.org/); OWASP_CRS/2.2.6.
Server: Apache
Engine-Mode: "DETECTION_ONLY"

--77c22b4e-Z--

ログを確認すると、
modsecurity_crs_41_sql_injection_attacks.conf の内容でフィルタリングされていることがわかるので、
SQL インジェクションのルールがパターンマッチしていることが分かりますね。

この状態で SecRuleEngine On と設定した場合、
SQL インジェクションのパラメータを付加してアクセスを行うと、
Apache のテストページへ飛ばされるはずです。


ルールセット一覧

SQL インジェクション以外にもデフォルトで多数のルールセットが有効になっています。

# ll /etc/httpd/modsecurity.d/activated_rules
合計 88
lrwxrwxrwx 1 root root 64  4月 19 16:41 2017 modsecurity_35_bad_robots.data -> /usr/lib/modsecurity.d/base_rules/modsecurity_35_bad_robots.data
lrwxrwxrwx 1 root root 62  4月 19 16:41 2017 modsecurity_35_scanners.data -> /usr/lib/modsecurity.d/base_rules/modsecurity_35_scanners.data
lrwxrwxrwx 1 root root 69  4月 19 16:41 2017 modsecurity_40_generic_attacks.data -> /usr/lib/modsecurity.d/base_rules/modsecurity_40_generic_attacks.data
lrwxrwxrwx 1 root root 75  4月 19 16:41 2017 modsecurity_41_sql_injection_attacks.data -> /usr/lib/modsecurity.d/base_rules/modsecurity_41_sql_injection_attacks.data
lrwxrwxrwx 1 root root 62  4月 19 16:41 2017 modsecurity_50_outbound.data -> /usr/lib/modsecurity.d/base_rules/modsecurity_50_outbound.data
lrwxrwxrwx 1 root root 70  4月 19 16:41 2017 modsecurity_50_outbound_malware.data -> /usr/lib/modsecurity.d/base_rules/modsecurity_50_outbound_malware.data
lrwxrwxrwx 1 root root 77  4月 19 16:41 2017 modsecurity_crs_20_protocol_violations.conf -> /usr/lib/modsecurity.d/base_rules/modsecurity_crs_20_protocol_violations.conf
lrwxrwxrwx 1 root root 76  4月 19 16:41 2017 modsecurity_crs_21_protocol_anomalies.conf -> /usr/lib/modsecurity.d/base_rules/modsecurity_crs_21_protocol_anomalies.conf
lrwxrwxrwx 1 root root 72  4月 19 16:41 2017 modsecurity_crs_23_request_limits.conf -> /usr/lib/modsecurity.d/base_rules/modsecurity_crs_23_request_limits.conf
lrwxrwxrwx 1 root root 69  4月 19 16:41 2017 modsecurity_crs_30_http_policy.conf -> /usr/lib/modsecurity.d/base_rules/modsecurity_crs_30_http_policy.conf
lrwxrwxrwx 1 root root 68  4月 19 16:41 2017 modsecurity_crs_35_bad_robots.conf -> /usr/lib/modsecurity.d/base_rules/modsecurity_crs_35_bad_robots.conf
lrwxrwxrwx 1 root root 73  4月 19 16:41 2017 modsecurity_crs_40_generic_attacks.conf -> /usr/lib/modsecurity.d/base_rules/modsecurity_crs_40_generic_attacks.conf
lrwxrwxrwx 1 root root 79  4月 19 16:41 2017 modsecurity_crs_41_sql_injection_attacks.conf -> /usr/lib/modsecurity.d/base_rules/modsecurity_crs_41_sql_injection_attacks.conf
lrwxrwxrwx 1 root root 69  4月 19 16:41 2017 modsecurity_crs_41_xss_attacks.conf -> /usr/lib/modsecurity.d/base_rules/modsecurity_crs_41_xss_attacks.conf
lrwxrwxrwx 1 root root 72  4月 19 16:41 2017 modsecurity_crs_42_tight_security.conf -> /usr/lib/modsecurity.d/base_rules/modsecurity_crs_42_tight_security.conf
lrwxrwxrwx 1 root root 65  4月 19 16:41 2017 modsecurity_crs_45_trojans.conf -> /usr/lib/modsecurity.d/base_rules/modsecurity_crs_45_trojans.conf
lrwxrwxrwx 1 root root 75  4月 19 16:41 2017 modsecurity_crs_47_common_exceptions.conf -> /usr/lib/modsecurity.d/base_rules/modsecurity_crs_47_common_exceptions.conf
lrwxrwxrwx 1 root root 82  4月 19 16:41 2017 modsecurity_crs_48_local_exceptions.conf.example -> /usr/lib/modsecurity.d/base_rules/modsecurity_crs_48_local_exceptions.conf.example
lrwxrwxrwx 1 root root 74  4月 19 16:41 2017 modsecurity_crs_49_inbound_blocking.conf -> /usr/lib/modsecurity.d/base_rules/modsecurity_crs_49_inbound_blocking.conf
lrwxrwxrwx 1 root root 66  4月 19 16:41 2017 modsecurity_crs_50_outbound.conf -> /usr/lib/modsecurity.d/base_rules/modsecurity_crs_50_outbound.conf
lrwxrwxrwx 1 root root 75  4月 19 16:41 2017 modsecurity_crs_59_outbound_blocking.conf -> /usr/lib/modsecurity.d/base_rules/modsecurity_crs_59_outbound_blocking.conf
lrwxrwxrwx 1 root root 69  4月 19 16:41 2017 modsecurity_crs_60_correlation.conf -> /usr/lib/modsecurity.d/base_rules/modsecurity_crs_60_correlation.conf

ちなみに追加のルールセットもインストールすることも出来ます。
どのようなルールセットが追加されるかは今回は割愛します。

# yum install mod_security_crs-extras

こんな感じに簡単に ModSecurity が導入出来ました!

ただ WAF の設定は非常に勘所が難しく、強化し過ぎるとアプリケーションに害を及ぼすし、
緩くし過ぎると脆弱性のリスクが高まるので、設定は随時見なおしていくのがよいかと思います。