mod_securityのインストールとWordPressの編集を可能にする設定
May 21, 2015 – 11:13 am最近、このブログに対し、WordPress の plugin の脆弱性を利用した不正アクセスが頻発している。今のところ、ブログへのアクセス解析するうえで多少の不具合はあるものの、こうした不正アクセスによる実害は認められないが、実際に被害を被る前に何らかの対策を講じるに越したことはない。
こうした不正アクセスに対処するためには、クライアント側から送られてくるHTTPリクエスト、それに含まれるデータ等を評価・解析し、それに基づき、そのリクエストを受け入れるか否かを判定する仕組みが必要だ。こうした仕組みとして、WAF(Web Application Firewall)と呼ばれるツールがあり、WAFのうちオープンソースとしてmod_security が利用可能になっている。我がサーバにも、今回、このmod_securityを導入し、サーバのセキュリティを向上させる措置をとることにした。
このエントリーでは、今回実施した一連の作業、即ち、mod_securityのインストール、インストール後のWordPress動作に係る設定上の作業、そして、このツールを用いた上記の不正アクセスに対する対処、についてメモしておいた。
WordPress Pluginの脆弱性を狙った不正アクセス:
ここ数週間、本ブログへのアクセスに不正なアクセスをにおわせるものが認められた。access-logの一部を以下に示す:
blue38.dnsmisitio.net - - [10/May/2015:04:48:34 +0900] "GET /archives/Post-693.html/wp-admin/admin-ajax.php?action=revslider_show_image&img=../wp-config.php HTTP/1.1" 404 43045 "-" "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.2) Gecko/20100115 Firefox/3.6" blue38.dnsmisitio.net - - [10/May/2015:04:48:37 +0900] "GET /archives/Post-693.html/wp-admin/admin-ajax.php?action=revslider_show_image&img=../wp-config.php HTTP/1.1" 404 43045 "-" "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.2) Gecko/20100115 Firefox/3.6"
このログレコードに含まれる revslider_show_image はWordPressのPluginのひとつで、その脆弱性を利用した不正アクセスの存在が報告されている(報告例)。上記ログから、この不正アクセスにより、WordPressの環境設定ファイル wp-config.php が読み取られようとされていることが推察される。wp-config.php には、WordPress の背後で動作するSQLデータベースのユーザー名、パスワードなどが含まれており、これが外部から読み取られるとブログの改ざんなどにつながる。
ブログのaccess-logには、上記したログとリクエストの内容を同一とするが、複数の異なるアクセス元が記録されている。この事実は、多数の計算機が踏み台とされ不正アクセスのアクセス元になっていることを窺わさせる。
不正アクセスに対する実効的な対処方策:
上述したアタックに対処するには、サーバー上で動作するウェブアプリケーション自身をセキュアにすることに加えて、アプリケーションに送られてくるリクエスト、データの内容を評価・解析し、セキュリティ上問題があるものについては、アクセスを許可しないといった仕組みが必要である。こうした仕組みとして、WAF(Web Application Firewall)と呼ばれる仕組み、ツールが知られている。
WAFの役割は「クライアントからWebサーバに向けて送信されるデータおよび入力のチェックを行い、不正な入力がWebサーバーに渡されないようにする」こととされる。そこでWAFはWebアプリケーションが稼働するサーバの前段階に配置され、不正な入力があれば通信を遮断し、内部のWebアプリケーションを防御する。
WAFのオープンソフトプロダクトとして mod_security がある。これはApacheのモジュールとして動作し、不正アクセスやワームなどからApacheを守るソフトウェアであり、GPLライセンスのもと利用可能になっている。
mod_securyityのインストール:
mod_securityを、epelリポジトリを使用しyum インストールした。インストール時のログを以下に掲げる:
[root@ ]# yum --enablerepo=epel install mod_security mod_security_crs Loaded plugins: refresh-packagekit, security Setting up Install Process epel/metalink | 5.2 kB 00:00 epel | 4.4 kB 00:00 epel/primary_db | 6.5 MB 00:04 Resolving Dependencies --> Running transaction check ---> Package mod_security.x86_64 0:2.7.3-3.el6 will be installed ---> Package mod_security_crs.noarch 0:2.2.6-3.el6 will be installed --> Finished Dependency Resolution Dependencies Resolved ==================================================================================================================== Package Arch Version Repository Size ==================================================================================================================== Installing: mod_security x86_64 2.7.3-3.el6 epel 168 k mod_security_crs noarch 2.2.6-3.el6 epel 92 k Transaction Summary ==================================================================================================================== Install 2 Package(s) Total download size: 260 k Installed size: 801 k Is this ok [y/N]: y Downloading Packages: (1/2): mod_security-2.7.3-3.el6.x86_64.rpm | 168 kB 00:00 (2/2): mod_security_crs-2.2.6-3.el6.noarch.rpm | 92 kB 00:00 -------------------------------------------------------------------------------------------------------------------- Total 733 kB/s | 260 kB 00:00 Running rpm_check_debug Running Transaction Test Transaction Test Succeeded Running Transaction Installing : mod_security-2.7.3-3.el6.x86_64 1/2 Installing : mod_security_crs-2.2.6-3.el6.noarch 2/2 Verifying : mod_security_crs-2.2.6-3.el6.noarch 1/2 Verifying : mod_security-2.7.3-3.el6.x86_64 2/2 Installed: mod_security.x86_64 0:2.7.3-3.el6 mod_security_crs.noarch 0:2.2.6-3.el6 Complete!
このログに見られるように、インストールにあたっては mod_security本体に加え、OWASP Modsecurity Core Rule Set (mod_security_crs)をインストールしている。
mod_security インストール時の若干の注意点:
apacheのモジュールmod_securityをインストールしたことから、このモジュールを配置したapache の再起動を行なったところ、apacheの起動に失敗し、以下のエラーが発生した。
[Sat May 16 09:35:38 2015] [alert] (EAI 2)Name or service not known: mod_unique_id: unable to find IPv4 address of "yamasserver01" Configuration Failed
このエラーは、mod_securityがインストールされたことにより apache が server の host名を、直接、参照するようになったことによるものと推察した。このエラーをクリアするには、/etc/hosts 上で、server の host名とIPアドレスを対応付けることが必要になる。/etc/hostsを以下のように変更した。
/etc/hosts:
変更前:
127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4 ::1 localhost localhost.localdomain localhost6 localhost6.localdomain6変更後:
127.0.0.1 yamasserver01 localhost localhost.localdomain localhost4 localhost4.localdomain4 ::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
WordPressの管理画面での操作(ポストの新規投稿・編集など)を可能にする:
mod_securityインストールの際に導入したルールセットのもとでは、WordPressの管理画面上でのポストの新規登録、編集などを行うことはできない。これは、WordPressにおいてこの種の操作を行おうとすると、mod_securityがこれを不正なアクセスと判定してしまうことによる。運用上、こうした事態をさけるためには、WordPressの通常の操作を「不正」な操作として認識するルールを除去しておけば良い。
除去対象とすべきルールの同定、そしてmod_securityを有効にする手順を以下に示す:
- /etc/httpd/conf.d/mod_security.conf を編集し、SecRuleEngine をDetectionOnlyにする
SecRuleEngine DetectionOnly
- apacheを再起動する
[root ]# service httpd restart Stopping httpd: [ OK ] Starting httpd: [ OK ]
- WordPress上で想定される管理画面へのアクセス、ポストの新規登録、編集などを含む一連の通常操作を行う
- 前記操作に伴い出力されるログ(/var/log/httpd/modsec_audit.log)上に記録されているルールのidをピックアップし、このidに対応するルールをConfiguration Directive のひとつSecRuleRemoveByIdを用いて除去( mod_security.confの最終行にピックアップしたid対応のルール除去に対応するためのレコードを付加し、SecRuleEngineをOn)。
modsec_audit.log出力の一部(例示のみ):
--07e30245-A-- [20/May/2015:13:55:08 +0900] VVwTrH8AAAEAAAHSNs4AAAAM 153.217.21.197 53968 192.168.11.111 80 --07e30245-B-- POST /wp-admin/admin-ajax.php HTTP/1.1 Accept: */* Content-Type: application/x-www-form-urlencoded; charset=UTF-8 X-Requested-With: XMLHttpRequest Referer: http://wptest.yamasnet.com/wp-admin/post.php?post=30&action=edit Accept-Language: ja-JP Accept-Encoding: gzip, deflate User-Agent: Mozilla/5.0 (Windows NT 6.1; Trident/7.0; rv:11.0) like Gecko Host: wptest.yamasnet.com Content-Length: 92 DNT: 1 Connection: Keep-Alive Cache-Control: no-cache Cookie: wordpress_80f1fb8b54362acb8a60a269821955f2=test.password%7C1432270402%7CgNS640khfgyeNUBy56iFQ5lb9KZfpodmZ3f3IEU83gE%7Ccec2931cdab4a3878a409469cfbcc2f59bcda0f8d3c7040994b5d7359f5c0465; wordpress_test_cookie=WP+Cookie+check; wordpress_logged_in_80f1fb8b54362acb8a60a269821955f2=test.password%7C1432270402%7CgNS640khfgyeNUBy56iFQ5lb9KZfpodmZ3f3IEU83gE%7Cc2eeac349f7b8429bc4115ebad9e8e953912ff42c8c024c175f3957da560c696; wp-settings-1=libraryContent%3Dbrowse%26urlbutton%3Dpost%26mfold%3Df%26cats%3Dpop%26editor%3Dhtml; wp-settings-time-1=1432097703 --07e30245-C-- action=wordtwit_ajax&wordtwit_action=are-hash-tags-enabled&wordtwit_nonce=2e984b424b&post=30 --07e30245-F-- HTTP/1.1 200 OK X-Powered-By: PHP/5.3.3 X-Robots-Tag: noindex X-Content-Type-Options: nosniff Expires: Wed, 11 Jan 1984 05:00:00 GMT Cache-Control: no-cache, must-revalidate, max-age=0 Pragma: no-cache X-Frame-Options: SAMEORIGIN Content-Length: 3 Keep-Alive: timeout=15, max=96 Connection: Keep-Alive Content-Type: text/html; charset=UTF-8 --07e30245-E-- --07e30245-H-- Message: Warning. Pattern match "([\\~\\!\\@\\#\\$\\%\\^\\&\\*\\(\\)\\-\\+\\=\\{\\}\\[\\]\\|\\:\\;\"\\'\\\xc2\xb4\\\xe2\x80\x99\\\xe2\x80\x98\\`\\<\\>].*?){8,}" at REQUEST_COOKIES:wp-settings-1. [file "/etc/httpd/modsecurity.d/activated_rules/modsecurity_crs_41_sql_injection_attacks.conf"] [line "168"] [id "981172"] [rev "2"] [msg "Restricted SQL Character Anomaly Detection Alert - Total # of special characters exceeded"] [data "Matched Data & found within REQUEST_COOKIES:wp-settings-1: libraryContent=browse&urlbutton=post&mfold=f&cats=pop&editor=html"] [ver "OWASP_CRS/2.2.6"] [maturity "9"] [accuracy "8"] Message: Warning. Operator LT matched 5 at TX:inbound_anomaly_score. [file "/etc/httpd/modsecurity.d/activated_rules/modsecurity_crs_60_correlation.conf"] [line "33"] [id "981203"] [msg "Inbound Anomaly Score (Total Inbound Score: 3, SQLi=1, XSS=): Restricted SQL Character Anomaly Detection Alert - Total # of special characters exceeded"] [ver "OWASP_CRS/2.2.6"] [maturity "9"] [accuracy "8"] Apache-Handler: php5-script Stopwatch: 1432097708528204 44880 (- - -) Stopwatch2: 1432097708528204 44880; combined=13126, p1=272, p2=12646, p3=3, p4=82, p5=122, sr=55, sw=1, l=0, gc=0 Response-Body-Transformed: Dechunked Producer: ModSecurity for Apache/2.7.3 (http://www.modsecurity.org/); OWASP_CRS/2.2.6. Server: Apache Engine-Mode: "DETECTION_ONLY"
/etc/httpd/mod_security.conf:
LoadModule security2_module modules/mod_security2.so <IfModule !mod_unique_id.c> LoadModule unique_id_module modules/mod_unique_id.so </IfModule> <IfModule mod_security2.c> # ModSecurity Core Rules Set configuration Include modsecurity.d/*.conf Include modsecurity.d/activated_rules/*.conf # Default recommended configuration SecRuleEngine On # SecRuleEngine Off # SecRuleEngine DetectionOnly SecRequestBodyAccess On SecRule REQUEST_HEADERS:Content-Type "text/xml" \ "id:'200000',phase:1,t:none,t:lowercase,pass,nolog,ctl:requestBodyProcessor=XML" SecRequestBodyLimit 13107200 SecRequestBodyNoFilesLimit 131072 SecRequestBodyInMemoryLimit 131072 SecRequestBodyLimitAction Reject SecRule REQBODY_ERROR "!@eq 0" \ "id:'200001', phase:2,t:none,log,deny,status:400,msg:'Failed to parse request body.',logdata:'%{reqbody_error_msg}' ,severity:2" SecRule MULTIPART_STRICT_ERROR "!@eq 0" \ "id:'200002', phase:2,t:none,log,deny,status:44,msg:'Multipart request body \ failed strict validation: \ failed strict validation: \ PE %{REQBODY_PROCESSOR_ERROR}, \ BQ %{MULTIPART_BOUNDARY_QUOTED}, \ BW %{MULTIPART_BOUNDARY_WHITESPACE}, \ DB %{MULTIPART_DATA_BEFORE}, \ DA %{MULTIPART_DATA_AFTER}, \ HF %{MULTIPART_HEADER_FOLDING}, \ LF %{MULTIPART_LF_LINE}, \ SM %{MULTIPART_MISSING_SEMICOLON}, \ IQ %{MULTIPART_INVALID_QUOTING}, \ IP %{MULTIPART_INVALID_PART}, \ IH %{MULTIPART_INVALID_HEADER_FOLDING}, \ FL %{MULTIPART_FILE_LIMIT_EXCEEDED}'" SecRule MULTIPART_UNMATCHED_BOUNDARY "!@eq 0" \ "id:'200003',phase:2,t:none,log,deny,status:44,msg:'Multipart parser detected a possible unmatched boundary.'" SecPcreMatchLimit 1000 SecPcreMatchLimitRecursion 1000 SecRule TX:/^MSC_/ "!@streq 0" \ "id:'200004',phase:2,t:none,deny,msg:'ModSecurity internal error flagged: %{MATCHED_VAR_NAME}'" SecResponseBodyAccess Off SecDebugLog /var/log/httpd/modsec_debug.log SecDebugLogLevel 0 SecAuditEngine RelevantOnly SecAuditLogRelevantStatus "^(?:5|4(?!04))" SecAuditLogParts ABIJDEFHZ SecAuditLogType Serial SecAuditLog /var/log/httpd/modsec_audit.log SecArgumentSeparator & SecCookieFormat 0 SecTmpDir /var/lib/mod_security SecDataDir /var/lib/mod_security SecRuleRemoveById 950001 SecRuleRemoveById 950005 SecRuleRemoveById 950010 SecRuleRemoveById 950109 SecRuleRemoveById 950901 SecRuleRemoveById 950908 SecRuleRemoveById 950910 SecRuleRemoveById 950911 SecRuleRemoveById 958007 SecRuleRemoveById 958039 SecRuleRemoveById 958051 SecRuleRemoveById 958052 SecRuleRemoveById 958413 SecRuleRemoveById 959073 SecRuleRemoveById 960009 SecRuleRemoveById 960015 SecRuleRemoveById 960024 SecRuleRemoveById 960912 SecRuleRemoveById 973300 SecRuleRemoveById 973302 SecRuleRemoveById 973303 SecRuleRemoveById 973304 SecRuleRemoveById 973305 SecRuleRemoveById 973306 SecRuleRemoveById 973316 SecRuleRemoveById 973330 SecRuleRemoveById 973331 SecRuleRemoveById 973332 SecRuleRemoveById 973333 SecRuleRemoveById 973335 SecRuleRemoveById 981172 SecRuleRemoveById 981173 SecRuleRemoveById 981204 SecRuleRemoveById 981231 SecRuleRemoveById 981240 SecRuleRemoveById 981242 SecRuleRemoveById 981243 SecRuleRemoveById 981244 SecRuleRemoveById 981245 SecRuleRemoveById 981246 SecRuleRemoveById 981248 SecRuleRemoveById 981249 SecRuleRemoveById 981251 SecRuleRemoveById 981257 SecRuleRemoveById 981260 SecRuleRemoveById 981317 </IfModule>
- apacheを再起動
[root ]# service httpd restart Stopping httpd: [ OK ] Starting httpd: [ OK ]
特定の不正アクセスへの対処法:
冒頭、このブログに対して Plugin revslider_show_imageの脆弱性を狙ってWordPressの環境設定ファイルwp-config.phpを読み取ろうとする不正アクセスについて述べた。このアタックに対応するHTTPリクエストのなかに、「reveslider_show_image」なる文字列が含まれることに着目し、この文字列を含むアクセスを拒否する「ルール」を付加することにした。
具体的な「ルール」は以下の通り:
SecRule REQUEST_URI|ARGS|REQUEST_BODY "revslider_show_image" "id:'210001',log,deny,msg:'Access Denied'"
これを前項で示した mod_security.conf の最終行に追記し、apacheを再起動することにより、reveslider_show_image の文字列を含むURIによるアクセスを廃除することができる。
ログファイル mdsec_audit.log において、これに該当する部分を以下に示す:
--41905a1d-A-- [17/May/2015:10:55:41 +0900] VVf1HX8AAAEAADOK@A8AAAAO 94.131.14.12 34408 192.168.11.111 80 --41905a1d-B-- GET /wp-admin/admin-ajax.php?action=revslider_show_image&img=../wp-config.php HTTP/1.1 Host: memorandum.yamasnet.com Connection: keep-alive Accept-Encoding: gzip, deflate Accept: */* User-Agent: Mozilla/5.0 (Windows NT 6.1; rv:34.0) Gecko/20100101 Firefox/34.0 --41905a1d-F-- HTTP/1.1 403 Forbidden Content-Length: 225 Keep-Alive: timeout=15, max=100 Connection: Keep-Alive Content-Type: text/html; charset=iso-8859-1 --41905a1d-E-- --41905a1d-H-- Message: Access denied with code 403 (phase 1). Pattern match "revslider_show_image" at REQUEST_URI. [file "/etc/httpd/conf.d/mod_security.conf"] [line "89"] [id "210001"] [msg "Access Denied"] Action: Intercepted (phase 1) Stopwatch: 1431827741049237 700 (- - -) Stopwatch2: 1431827741049237 700; combined=256, p1=225, p2=0, p3=0, p4=0, p5=30, sr=55, sw=1, l=0, gc=0 Response-Body-Transformed: Dechunked Producer: ModSecurity for Apache/2.7.3 (http://www.modsecurity.org/); OWASP_CRS/2.2.6. Server: Apache Engine-Mode: "ENABLED" --41905a1d-Z--
まとめ:
mod_securityのインストール、そしてその環境下でWordPressを運用するために施した諸設定に係り、その作業記録をメモしておいた。
今回のインストール作業を行う契機となったWordPressのPlugin revslider_show_imageの脆弱性を利用した不正アクセスに対し、mod_securityを活用して対処することができた。
mod_securityのルールセットで導入される防御策は、WordPressへの新規ポストの登録、編集を「不正」なものと判定されることがある。今回の作業のなかで、WordPressの運用上障害となる一定の「ルール」の除外について議論し、ほぼWordPressの一般的な運用にとっては問題のないレベルまで除外ルールを同定することができた。しかし、ルールによる「不正」判定がポストに書き込まれるデータ、特に特殊文字を含むかいなかに強く影響されることがあるので、注意が必要と思われる。
参考にしたサイト:
- Web Application Firewall(WAF)読本
- WordPressが動作しているCentOSにModSecurityを導入する
- ModSecurity OpenSourceWeb Application Firewall
One Response to “mod_securityのインストールとWordPressの編集を可能にする設定”
WordPressに投稿時は、一時的にmod_securityの機能をオフにし、投稿完了時にもとに戻す運用が望ましい。
mod_securityをオフにするには、
/etc/httpd/conf.d/mod_security.confに於いて
SecRuleEngineをOff にし、
apacheを再起動。
WordPressへの投稿終了時にもとに戻し、apacheを再起動。
By Yama on Jul 6, 2015