Hello, ModSecurity! - 実際に動作させてみよう

ModSecurityはApacheのモジュールとして動作する。設定はhttpd.confをはじめとしたApacheのコンフィグファイル上でおこなう。Portsからインストールすると、Apacheのコンフィグファイルを格納するディレクトリに関連するファイル(ModSecurity Core Rules)がひととおり生成される。デプロイされるディレクトリとファイルは次のとおり。

  • Includes/mod_security2.conf
  • Includes/mod_security2/CHANGELOG
  • Includes/mod_security2/LICENSE
  • Includes/mod_security2/README
  • Includes/mod_security2/modsecurity_crs_10_config.conf
  • Includes/mod_security2/modsecurity_crs_20_protocol_violations.conf
  • Includes/mod_security2/modsecurity_crs_21_protocol_anomalies.conf
  • Includes/mod_security2/modsecurity_crs_23_request_limits.conf
  • Includes/mod_security2/modsecurity_crs_30_http_policy.conf
  • Includes/mod_security2/modsecurity_crs_35_bad_robots.conf
  • Includes/mod_security2/modsecurity_crs_40_generic_attacks.conf
  • Includes/mod_security2/modsecurity_crs_45_trojans.conf
  • Includes/mod_security2/modsecurity_crs_50_outbound.conf
  • Includes/mod_security2/optional_rules/modsecurity_crs_20_protocol_violations.conf
  • Includes/mod_security2/optional_rules/modsecurity_crs_21_protocol_anomalies.conf
  • Includes/mod_security2/optional_rules/modsecurity_crs_40_generic_attacks.conf
  • Includes/mod_security2/optional_rules/modsecurity_crs_42_comment_spam.conf
  • Includes/mod_security2/optional_rules/modsecurity_crs_42_tight_security.conf
  • Includes/mod_security2/optional_rules/modsecurity_crs_55_marketing.conf

これらのファイルには詳細な説明文つきで設定内容が記載されている。上記ファイルを参考にしながら、対象のWebサーバにあった設定を試みていこう。ひとまずこれらファイルを用いて、どのような動作をするのか確認していく。

まず0バイトのPHPファイル「test.php」を用意し、ApacheのDocumentRoot以下にデプロイ。httpd.conf上でIncludes/mod_security2.confをロードするように設定をおこなう。Apacheの再起動後、Webブラウザでtest.phpにアクセスする。

index.phpにアクセス。0バイトのファイルなので当然何も表示されない

初期設定をそのまま使用した場合、ModSecurityは攻撃の検出とロギングのみをおこなう。ログは/var/log/以下のhttpd-modsec2_audit.logやhttpd-modsec2_debug.log、httpd-error.logに出力される。続けてクロスサイトスクリプティング(XSS)攻撃をおこなってみよう。

index.php?q=<script>alert(0)</script>にアクセス。何も表示されていないが、ModSecurityはこのXSS攻撃を検出している

「index.php?q=<script>alert(0)</script>」アクセス直後のhttpd-modsec2_audit.log

--ee46d31e-A--
[27/Sep/2009:18:10:33 +0900] Sr8sCcCoIXAAAPVoAZAAAAAA 192.168.33.15 61165 192.168.33.112 80
--ee46d31e-B--
GET /test.php?q=%3Cscript%3Ealert(0)%3C/script%3E HTTP/1.1
Host: mintia.localhost
User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_8; ja-jp) AppleWebKit/531.9 (KHTML, like Gecko) Version/4.0.3 Safari/531.9
Accept: application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Language: ja-jp
Accept-Encoding: gzip, deflate
Cookie: __utma=111872281.1807394839442337800.1251268704.1252382184.1253109746.16; __utmz=111872281.1251268704.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none)
Connection: keep-alive

--ee46d31e-F--
HTTP/1.1 200 OK
X-Powered-By: PHP/5.2.11
Content-Length: 0
Keep-Alive: timeout=5, max=100
Connection: Keep-Alive
Content-Type: text/html

--ee46d31e-E--

--ee46d31e-H--
Message: Warning. Pattern match "(?:\b(?:(?:type\b\W*?\b(?:text\b\W*?\b(?:j(?:ava)?|ecma|vb)|application\b\W*?\bx-(?:java|vb))script|c(?:opyparentfolder|reatetextrange)|get(?:special|parent)folder|iframe\b.{0,100}?\bsrc)\b|on(?:(?:mo(?:use(?:o(?:ver|ut)|down|move|up)|ve)|key(?:press|d ..." at ARGS:q. [file "/usr/local/etc/apache22/Includes/mod_security2/modsecurity_crs_40_generic_attacks.conf"] [line "102"] [id "950004"] [msg "Cross-site Scripting (XSS) Attack"] [data "<script"] [severity "CRITICAL"] [tag "WEB_ATTACK/XSS"]
Stopwatch: 1254042633847833 2597 (212 1705 2299)
Response-Body-Transformed: Dechunked
Producer: ModSecurity for Apache/2.5.9 (http://www.modsecurity.org/); core ruleset/1.6.1.
Server: Apache/2.2.13 (FreeBSD) mod_ssl/2.2.13 OpenSSL/0.9.8k DAV/2 PHP/5.2.11 with Suhosin-Patch

--ee46d31e-K--
SecRule "REQUEST_METHOD" "@rx ^(?:GET|HEAD)$" "phase:2,chain,t:none,deny,log,auditlog,status:400,msg:'GET or HEAD requests with bodies',severity:2,id:960011,tag:PROTOCOL_VIOLATION/EVASION"
SecRule "&REQUEST_HEADERS:Content-Type" "@eq 0" "phase:2,pass,chain,t:none,log,auditlog,msg:'Request Containing Content, but Missing Content-Type header',id:960904,severity:4"
SecAction "phase:2,auditlog,nolog,skipAfter:959009"
SecAction "phase:2,auditlog,nolog,skipAfter:959007"
SecAction "phase:2,auditlog,nolog,skipAfter:959904"
SecAction "phase:2,auditlog,nolog,id:999501,skipAfter:959001"
SecAction "phase:2,auditlog,nolog,skipAfter:959906"
SecRule "REQUEST_FILENAME|ARGS|ARGS_NAMES|REQUEST_HEADERS|XML:/*|!REQUEST_HEADERS:Referer" "@pm jscript onsubmit copyparentfolder javascript meta onmove onkeydown onchange onkeyup activexobject expression onmouseup ecmascript onmouseover vbscript: <![cdata[ http: settimeout onabort shell: .innerhtml onmousedown onkeypress asfunction: onclick .fromcharcode background-image: .cookie ondragdrop onblur x-javascript mocha: onfocus javascript: getparentfolder lowsrc onresize @import alert onselect script onmouseout onmousemove background application .execscript livescript: getspecialfolder vbscript iframe .addimport onunload createtextrange onload <input" "phase:2,auditlog,t:none,t:urlDecodeUni,t:htmlEntityDecode,t:compressWhiteSpace,t:lowercase,nolog,skip:1"
SecRule "REQUEST_FILENAME|ARGS|ARGS_NAMES|REQUEST_HEADERS|XML:/*|!REQUEST_HEADERS:Referer" "@pm jscript onsubmit copyparentfolder javascript meta onmove onkeydown onchange onkeyup activexobject expression onmouseup ecmascript onmouseover vbscript: <![cdata[ http: settimeout onabort shell: .innerhtml onmousedown onkeypress asfunction: onclick .fromcharcode background-image: .cookie ondragdrop onblur x-javascript mocha: onfocus javascript: getparentfolder lowsrc onresize @import alert onselect script onmouseout onmousemove background application .execscript livescript: getspecialfolder vbscript iframe .addimport onunload createtextrange onload <input" "phase:2,auditlog,t:none,t:urlDecodeUni,t:htmlEntityDecode,t:compressWhiteSpace,t:lowercase,nolog,skip:1"
SecRule "REQUEST_FILENAME|ARGS|ARGS_NAMES" "@rx (?:\\b(?:(?:type\\b\\W*?\\b(?:text\\b\\W*?\\b(?:j(?:ava)?|ecma|vb)|application\\b\\W*?\\bx-(?:java|vb))script|c(?:opyparentfolder|reatetextrange)|get(?:special|parent)folder|iframe\\b.{0,100}?\\bsrc)\\b|on(?:(?:mo(?:use(?:o(?:ver|ut)|down|move|up)|ve)|key(?:press|down|up)|c(?:hange|lick)|s(?:elec|ubmi)t|(?:un)?load|dragdrop|resize|focus|blur)\\b\\W*?=|abort\\b)|(?:l(?:owsrc\\b\\W*?\\b(?:(?:java|vb)script|shell|http)|ivescript)|(?:href|url)\\b\\W*?\\b(?:(?:java|vb)script|shell)|background-image|mocha):|s(?:(?:tyle\\b\\W*=.*\\bexpression\\b\\W*|ettimeout\\b\\W*?)\\(|rc\\b\\W*?\\b(?:(?:java|vb)script|shell|http):)|a(?:ctivexobject\\b|lert\\b\\W*?\\(|sfunction:))|<(?:(?:body\\b.*?\\b(?:backgroun|onloa)d|input\\b.*?\\btype\\b\\W*?\\bimage)\\b| ?(?:(?:script|meta)\\b|iframe)|!\\[cdata\\[)|(?:\\.(?:(?:execscrip|addimpor)t|(?:fromcharcod|cooki)e|innerhtml)|\\@import)\\b)" "phase:2,pass,capture,t:none,t:htmlEntityDecode,t:compressWhiteSpace,t:lowercase,ctl:auditLogParts=+E,log,auditlog,msg:'Cross-site Scripting (XSS) Attack',id:950004,tag:WEB_ATTACK/XSS,logdata:%{TX.0},severity:2"
SecAction "phase:2,auditlog,nolog,skipAfter:959005"
SecAction "phase:2,auditlog,nolog,skipAfter:950006"
SecRule "REQUEST_HEADERS|XML:/*|!REQUEST_HEADERS:'/^(Cookie|Referer|X-OS-Prefs)$/'|REQUEST_COOKIES|REQUEST_COOKIES_NAMES" "@pm uname wguest.exe /perl /nasm rcmd.exe nc tclsh /xterm finger tftp chown /echo nmap.exe ping /passwd /chsh ps /uname telnet.exe /ftp ls tclsh8 lsof /ping echo cmd.exe /kill python traceroute /ps perl passwd wsh.exe /rm /cpp chgrp /telnet localgroup kill /chgrp /finger nasm /ls nc.exe id /chmod /nc /g++ /id /chown cmd /nmap chsh /gcc net.exe /python /lsof ftp.exe ftp xterm mail /mail tracert nmap rm cd chmod cpp telnet cmd32.exe gcc g++" "phase:2,auditlog,t:none,t:urlDecodeUni,t:htmlEntityDecode,t:lowercase,nolog,skip:1"
SecAction "phase:2,auditlog,nolog,skipAfter:959013"

--ee46d31e-Z--

「index.php?q=<script>alert(0)</script>」アクセス直後のhttpd-error.log

[Sun Sep 27 18:10:33 2009] [error] [client 192.168.33.15] ModSecurity: Warning. Pattern match "(?:\\b(?:(?:type\\b\\W*?\\b(?:text\\b\\W*?\\b(?:j(?:ava)?|ecma|vb)|application\\b\\W*?\\bx-(?:java|vb))script|c(?:opyparentfolder|reatetextrange)|get(?:special|parent)folder|iframe\\b.{0,100}?\\bsrc)\\b|on(?:(?:mo(?:use(?:o(?:ver|ut)|down|move|up)|ve)|key(?:press|d ..." at ARGS:q. [file "/usr/local/etc/apache22/Includes/mod_security2/modsecurity_crs_40_generic_attacks.conf"] [line "102"] [id "950004"] [msg "Cross-site Scripting (XSS) Attack"] [data "<script"] [severity "CRITICAL"] [tag "WEB_ATTACK/XSS"] [hostname "mintia.localhost"] [uri "/test.php"] [unique_id "Sr8sCcCoIXAAAPVoAZAAAAAA"]

それぞれのログファイルに「どのIPアドレスからの」「どのアクセスが」「どの設定ファイルのルールと一致した」かが記録された。この場合「Cross-site Scripting (XSS) Attack」と一目でわかるようになっている。

今度は検出するのみではなく、実際にアクセスを弾いてみるまでを設定してみよう。ModSecurityの全般な設定はIncludes/mod_security2/modsecurity_crs_10_config.confにまとまっている。エディタで該当ファイルを開き、53行目の「SecRuleEngine」を"DetectionOnly"から"On"に変更、103行目の「SecDefaultAction」の行頭のコメントアウトを削除する。

修正前のファイル - modsecurity_crs_10_config.conf

53行目 SecRuleEngine DetectionOnly
103行目 #SecDefaultAction "phase:2,log,deny,status:403,t:lowercase,t:replaceNulls,t:compressWhitespace"

修正後のファイル - modsecurity_crs_10_config.conf

53行目 SecRuleEngine On 
103行目 SecDefaultAction "phase:2,log,deny,status:403,t:lowercase,t:replaceNulls,t:compressWhitespace"

編集後にApacheを再起動し、再度XSS攻撃をおこなってみる。

index.php?q=<script>alert(0)</script>にアクセス。403が返り、test.phpは実行されなかった

万が一Webアプリケーション側に脆弱性があったとしても、ModSecurityを設定しておけばさまざまな攻撃を未然に防ぐことが可能だ。今回のXSS攻撃はmodsecurity_crs_40_generic_attacks.conf内の次のルールに一致した。

SecRule REQUEST_FILENAME|ARGS|ARGS_NAMES "(?:\b(?:(?:type\b\W*?\b(?:text\b\W*?\b(?:j(?:ava)?|ecma|vb)|application\b\W*?\bx-(?:java|vb))script|c(?:opyparentfolder|reatetextrange)|get(?:special|parent)folder|iframe\b.{0,100}?\bsrc)\b|on(?:(?:mo(?:use(?:o(?:ver|ut)|down|move|up)|ve)|key(?:press|down|up)|c(?:hange|lick)|s(?:elec|ubmi)t|(?:un)?load|dragdrop|resize|focus|blur)\b\W*?=|abort\b)|(?:l(?:owsrc\b\W*?\b(?:(?:java|vb)script|shell|http)|ivescript)|(?:href|url)\b\W*?\b(?:(?:java|vb)script|shell)|background-image|mocha):|s(?:(?:tyle\b\W*=.*\bexpression\b\W*|ettimeout\b\W*?)\(|rc\b\W*?\b(?:(?:java|vb)script|shell|http):)|a(?:ctivexobject\b|lert\b\W*?\(|sfunction:))|<(?:(?:body\b.*?\b(?:backgroun|onloa)d|input\b.*?\btype\b\W*?\bimage)\b| ?(?:(?:script|meta)\b|iframe)|!\[cdata\[)|(?:\.(?:(?:execscrip|addimpor)t|(?:fromcharcod|cooki)e|innerhtml)|\@import)\b)" \
        "phase:2,capture,t:none,t:htmlEntityDecode,t:compressWhiteSpace,t:lowercase,ctl:auditLogParts=+E,log,auditlog,msg:'Cross-site Scripting (XSS) Attack',id:'950004',tag:'WEB_ATTACK/XSS',logdata:'%{TX.0}',severity:'2'"

SecRuleはModSecurityのメインとなるディレクティブ。以前のバージョンでは「SecFilter」と表記されていた。次の書式で記述する。

SecRule VARIABLES OPERATOR [ACTIONS]

それぞれの引数については次のとおり。

VARIABLES

チェックをおこなう変数を指定する。よく使用することになるREQUEST_HEADERSやARGSをはじめ、77種類の変数名が用意されている。詳細はVariablesの項を参照されたい。

OPERATOR

フィルタリングルールを正規表現で記述する。

ACTIONS

フィルタリングルールに一致した場合の挙動や、ロギング情報を定義する。省略した場合はSecDefaultActionで定義されたアクションが使用される。

SecRule以外にもおおくの有用なディレクティブが用意されている。別途マニュアルConfiguration Directivesの項を参照してほしい。

Hello, ModSecurity! - 初期状態で検出している項目について

インストール時にデプロイされたコンフィグファイルは、いわば区分けされたSecRuleのあつまりだ。これらのファイルで定義・検出している項目は次のとおり。

modsecurity_crs_20_protocol_violations.conf

  • Invalid HTTP Request Line
  • HTTP Request Smuggling Attack
  • Request Body Parsing Failed
  • Content-Length HTTP header is not numeric
  • GET or HEAD requests with bodies
  • POST request must have a Content-Length header
  • ModSecurity does not support transfer encodings
  • URL Encoding Abuse Attack Attempt
  • Unicode Full/Half Width Abuse Attack Attempt
  • Proxy access attempt
  • Invalid character in request

modsecurity_crs_21_protocol_anomalies.conf

  • Request Missing a Host Header
  • Request Missing an Accept Header
  • Request Missing a User Agent Header
  • Request Containing Content, but Missing Content-Type header
  • Host header is a numeric IP address
  • Invalid request

modsecurity_crs_23_request_limits.conf

  • Too many arguments in request

modsecurity_crs_30_http_policy.conf

  • Method is not allowed by policy
  • Request content type is not allowed by policy
  • HTTP protocol version is not allowed by policy
  • URL file extension is restricted by policy
  • HTTP header is restricted by policy
  • ModSecurity does not support content encodings

modsecurity_crs_35_bad_robots.conf

  • Request Indicates a Security Scanner Scanned the Site
  • Rogue web site crawler
  • Request Indicates an automated program explored the site

modsecurity_crs_40_generic_attacks.conf

  • Session Fixation
  • Blind SQL Injection Attack
  • SQL Injection Attack
  • Cross-site Scripting (XSS) Attack
  • Remote File Access Attempt
  • System Command Access
  • System Command Injection
  • Injection of Undocumented ColdFusion Tags
  • LDAP Injection Attack
  • SSI injection Attack
  • PHP Injection Attack
  • Persistent Universal PDF XSS attack
  • Email Injection Attack
  • HTTP Response Splitting Attack

modsecurity_crs_45_trojans.conf

  • Backdoor access

modsecurity_crs_50_outbound.conf

  • Statistics Information Leakage
  • SQL Information Leakage
  • IIS Information Leakage
  • Zope Information Leakage
  • Cold Fusion Information Leakage
  • PHP Information Leakage
  • ISA server existence revealed
  • Microsoft Office document properties leakage
  • Directory Listing
  • ASP/JSP source code leakage
  • PHP source code leakage
  • Cold Fusion source code leakage
  • IIS installed in default location
  • The application is not available
  • WebLogic information disclosure
  • File or Directory Names Leakage

mod_security2.confをそのままロードすると、これらの項目すべてが検出されるようになる。なかにはホスト名がIPアドレスの場合、アクセス不能になってしまう項目もあるので必要なものだけを抜き出して使うようにしよう。

管理者の知らぬ間にセキュリティ不備のあるWebアプリケーションがデプロイされていても、ModSecurityをインストールし適切に設定しておくことで、ある程度の予防線をはることができる。とくに昨今はWebアプリケーションの需要が高く、危険にさらされる機会もおおい。用意されているすべての機能を使いこなせるようになるまでには苦労しそうだが、覚えておいて損はないプロダクトだろう。

ただ「とりえあずModSecurityをインストールしたからもう大丈夫」と安心してしまうことだけは避けたい。「どのような攻撃の手法があり」「どのような実装がセキュリティホールとなり得るか」をサーバ管理者やWebデベロッパが理解し、防衛策を実際にはれることが大事だ。使用前にドキュメントや各種コンフィグファイルのコメントを熟読しておきたいところだ。これらの点を踏まえ、WebサーバにApacheを使用しているユーザ/デベロッパは、導入を検討してみてはいかがだろうか。