■ 河南 郭建偉
編者按: 在很多Web應用中,外部可以利用傳參的方式,來指定服務器中的文件名,例如在外界傳入?yún)?shù)時指定一個模版文件名,通過讀取模版文件的內(nèi)容,將其內(nèi)容在網(wǎng)頁中展示等。這不可避免的會導致出現(xiàn)一些安全隱患,例如黑客可以非法訪問Web服務器中的文件,來讀取服務器中的敏感文件并展示在網(wǎng)頁中等。黑客甚至還可以執(zhí)行OS命令注入,在服務器上非法執(zhí)行各種命令等。
當在Web應用中允許外界以參數(shù)的形式來指定服務器上的文件名時,如果沒有對文件名進行充分的校驗,就可能出現(xiàn)文件被非法瀏覽,甚至被惡意修改和刪除等,這其實就是大家常說的目錄遍歷漏洞。
實際上,在網(wǎng)上有很多Web服務器都存在該漏洞,其危害是不可忽視的。該漏洞會泄露服務器中的重要信息,黑客利用該漏洞可以對網(wǎng)頁進行篡改,發(fā)布各種惡意信息,利用非法鏈接將用戶誘至預設的惡意網(wǎng)站,對服務器配置文件進行破壞,導致服務器運行異常。
黑客還可能修改重要的腳本文件,在服務器上執(zhí)行各種惡意操作??梢哉f,凡是能夠由外界指定文件名的頁面,都可能存在該漏洞,其會對所有頁面產(chǎn)生影響。
對該漏洞進行的防范的要點在于對外界指定的文件名進行嚴格的限制,例如禁止由外界指定文件名,如果必須指定的話,文件名中不能包含目錄名,即只允許外界讀取指定目錄的內(nèi)容,禁止隨意切換目錄,限制文件名中僅包含字母和數(shù)字,這樣外界用戶就無法切換目錄,因為目錄名必須包含斜杠等特殊字符。
對該漏洞進行防御,最根本的策略是避免由外界指定文件名,例如將文件名固定,將文件名保存在會話變量中,不直接指定文件名而使用編號等方法間接指定等。例如,在某網(wǎng)站中存在“”,“
”,“”等語句,來讀取外部指定的模版文件,按照正常情況,當提交了合法的文件名后,就可以從指定的目錄中讀取該文件,將其內(nèi)容顯示在頁面中。但當攻擊者指定文件名,就會將密碼文件顯示出來,這對系統(tǒng)安全構(gòu)成了威脅。將上述網(wǎng)頁代碼修改為“”,“if ($ tmp_no ==1)”,“{$tmpl = "1.html"}”,“else”,“{die(' 只支持文件編號!')}”等,根據(jù)編號來讀取指定的文件,這樣就避免了上述漏洞。如果必須由外界指定文件名,那么文件名中不能包括目錄名(例如“../”等),就可以盡量降低目錄遍歷漏洞產(chǎn)生的風險。表示目錄 的 字 符 包 括“/”,“”?等,根據(jù)系統(tǒng)的不同存在差異。例如將上述程序修改為”等,利用指定函數(shù)來讀取傳入文件名,對其分析后得到末尾的文件名。
這樣,即使黑客傳入了非法文件名,其中包含的雜亂信息會被自動過濾,Web程序只得到末尾的文件名,當進行讀取時,只能得到錯誤信息。對傳入文件名的內(nèi)容進行限制,只允許包含字母和數(shù)字,也可以防御該漏洞。
在有些網(wǎng)站中,因為誤操作等原因,在公開目錄中有時可能存儲著對外保密的文件。這樣外界用戶只要知道了文件地址,就可以輕松的瀏覽其內(nèi)容。為防止出現(xiàn)上述情況,可以采取盡量不要在公開目錄放置敏感文件,對文件設置合適的訪問權(quán)限,或者直接禁用目錄列表功能等方法來解決該問題。
如果目錄列表功能沒有被禁用,當使用URL指定目錄名稱時,網(wǎng)頁上會顯示目錄中的所有文件。例如在Web服務器上執(zhí)行“vim/etc/httpd/conf/httpd.conf”命令,可看到公開目錄信息。在對應的“”等語句中顯示對應公開目錄的屬性信息,其中“xxx”表示目錄名稱,在“Options”欄中應該只包含“Includes ExecCGI FollowSymLinks”內(nèi)容,而禁止包含“Indexs”字樣,這樣該公開目錄的列表功能就被禁用了。
此外,對公開目錄中的敏感文件名要進行必要處理,例如禁止包含日期、用戶名或連續(xù)數(shù)值等易于被猜到的內(nèi)容,避免使用諸如“user.dat”、“data.txt”常用文件名等。關(guān)閉錯誤信息顯示功能也可在一定程序上避免暴露敏感文件。例如,在PHP配置文件中輸入“display_errors = off”行,關(guān)閉錯誤信息顯示功能。
當然,最根本的方法還是在設計程序時將重要文件存放到安全目錄,在租用服務器時確認可以使用非公開目錄等。
在使用PHP等語言開發(fā)Web程序時,在很多情況下需要使用一些系統(tǒng)級的命令來實現(xiàn)某些功能。即通過Shell執(zhí)行操作系統(tǒng)命令,或者在開發(fā)中用到的某個方法時,在其內(nèi)部會利用Shell來執(zhí)行OS命令等。
在執(zhí)行這類操作時,就可能出現(xiàn)操作系統(tǒng)命令被任意執(zhí)行的問題。例如在網(wǎng)頁中需要發(fā)送郵件時,會使用Linux中 的“sendmail”命令來實現(xiàn)。
例如,在某網(wǎng)頁中存在“$mail = $_Post['mail']”、“system("/usr/sbin/sendmail -i 對OS注入漏洞進行分析,可以發(fā)現(xiàn)在相關(guān)程序內(nèi)部調(diào)用OS命令,多數(shù)是通過Shell來啟動命令的。 Shell是用來操作OS的命令行界面,例如Windows中的“cmd.exe”,Linux中的“sh”、“bash”、“csh”等。通過Shell來啟動命令,能夠使得管道命令或者重定向等功能的使用變得更加快捷。但是,Shell提供的遍歷功能有時卻成為了OS注入的根源。 并且Shell提供了一次啟動多個命令的語法,因此,黑客就可能在參數(shù)中添加非法內(nèi)容,在原來命令的基礎上讓其他的命令被隨意執(zhí)行,這樣OS命令注入就產(chǎn)生了。 在Shell中提供了在一個命令行啟動多個程序的方法,其中OS命令注入就是惡意利用了該特性。在Windows中的“cmd.exe”程序可以利用“&”字符來連續(xù)執(zhí)行多個命令。此外,還可以 使 用“|”、“&&”、“||”等字符來執(zhí)行連續(xù)執(zhí)行多個命令等。 在Shell中擁有特殊意義的上述字符被稱為元字符,將其作為普通字符使用時需要進行轉(zhuǎn)義。如果在指定的OS命令參數(shù)中的字符串中混入了Shell元字符,就會讓攻擊者非法添加的命令得以順利執(zhí)行。 而若要產(chǎn)生OS注入漏洞,則必須滿足一些條件,例如包括使用了內(nèi)部調(diào)用的Shell的函數(shù)(例如“system”、“open”等),將外界傳入的參數(shù)傳遞給內(nèi)部調(diào)用的Shell的函數(shù),參數(shù)中的Shell的元字符沒有被轉(zhuǎn)義等。 為了防御OS注入漏洞,可以使用各種方法靈活應對。例如選擇不調(diào)用OS命令的實現(xiàn)方法,即在程序中不通過Shell來調(diào)用系統(tǒng)命令。不要將外界輸入的字符串傳遞給命令行參數(shù),使用安全的函數(shù)對傳遞來的參數(shù)進行轉(zhuǎn)義等。例如對于上述存在問題的代碼來說,可以不使用系統(tǒng)提供的“senmail”命令來實現(xiàn),使用PHP提供的“mb_send_mail”函數(shù),也可以實現(xiàn)郵件發(fā)送功能。 例如將其修改為“”等語句,來執(zhí)行郵件發(fā)送功能。這樣,就徹底消除了OS注入漏洞的威脅,還消除了調(diào)用OS命令的系統(tǒng)開銷,從多個方面提高了應用的性能。 如果必須通過Shell調(diào)用OS命令,或者并不了解所使用的函數(shù)內(nèi)部是否使用了Shell的話,那么不將參數(shù)傳遞給命令行,是防御OS命令注入的有效對策。例如對于“sendmail”命令來說,可以使用“-t”參數(shù),讓收件人地址不在命令行中指定,而是從郵件的消息頭中讀取。這樣就沒有必要將外界輸入的參數(shù)字符串指派給命令行,即消除了OS命令注入漏洞。 例如將上述代碼修改為“ 注意,為了防止郵件頭注入漏洞,這里對用戶輸入的郵箱地址進行了校驗,禁止其包含非法內(nèi)容。但是,在調(diào)用“sendmal”命令時,沒有任何外界傳入的參數(shù),OS命令注入漏洞自然無法成立。 當然,對外界傳入的OS命令的參數(shù)進行轉(zhuǎn)義處理,也可以有效消除OS命令注入漏洞。例如將上述代碼修改為“$mail= $_Post['mail']”、“system("/usr/sbin/sendmail -i OS注入漏洞產(chǎn)生的根源
防御OS注入的對策