在實(shí)際的項(xiàng)目開(kāi)發(fā)過(guò)程中,我們常常用提供表單之類(lèi)的文件上傳功能,在laravel社區(qū)看到一篇關(guān)于PHP 安全的指南,講述如何防范用戶(hù)上傳 PHP 可執(zhí)行文件。
上傳文件往往在處理不當(dāng)?shù)那闆r下,確實(shí)會(huì)帶來(lái)比較大的安全風(fēng)險(xiǎn),特別是用戶(hù)偽造信息,通過(guò)一些特殊請(qǐng)求讓上傳php可執(zhí)行文件,從而引發(fā)網(wǎng)站的安全問(wèn)題。
那么如何有效的防止用戶(hù)上傳php可執(zhí)行文件呢?筆者摘錄了該問(wèn)題的解決方法,供大家參考:
為了解決這個(gè)問(wèn)題,PHP中內(nèi)置了一些特殊函數(shù)方法,只是為了在這種情況下使用。
basename()
第一個(gè)解決方案?---?basename() 它從路徑結(jié)束時(shí)提取路徑的一部分,直到它遇到第一個(gè)斜杠,但忽略字符串末尾的斜杠,參見(jiàn)示例。無(wú)論如何,你會(huì)收到一個(gè)安全的文件名。如果你覺(jué)得安全 - 那么是的這很安全。如果它被不法上傳利用 - 你可以使用它來(lái)校驗(yàn)文件名是否安全。
realpath()
另一個(gè)解決方案?---?realpath()它將上傳文件路徑轉(zhuǎn)換規(guī)范化的絕對(duì)路徑名,從根開(kāi)始,并且根本不包含任何不安全因素。它甚至?xí)⒎?hào)鏈接轉(zhuǎn)換為此符號(hào)鏈接指向的路徑。
因此,您可以使用這兩個(gè)函數(shù)來(lái)檢查上傳文件的路徑。要檢查這個(gè)文件路徑到底是否真正屬于此文件夾路徑。
函數(shù)代碼
編寫(xiě)了一個(gè)函數(shù)來(lái)提供如上的檢查。我并不是專(zhuān)家,所以風(fēng)險(xiǎn)請(qǐng)自行承擔(dān)。代碼如下。
結(jié)語(yǔ)
必須過(guò)濾用戶(hù)輸入,文件名也屬于用戶(hù)輸入,所以一定要檢查文件名。記得使用 basename() 。
必須檢查你想存放用戶(hù)文件的路徑,永遠(yuǎn)不要將這個(gè)路徑和應(yīng)用目錄混合在一起。文件路徑必須由某個(gè)文件夾的字符串路徑,以及 basename($filename) 組成。文件被寫(xiě)入之前,一定要檢查最終組成的文件路徑。
在你引用某個(gè)文件前,必須檢查路徑,并且是嚴(yán)格檢查。
記得使用一些特殊的函數(shù),因?yàn)槟憧赡懿⒉涣私饽承┤觞c(diǎn)或漏洞。
并且,很明顯,這與文件后綴或 mime-type 無(wú)關(guān)。JPEG 允許字符串存在于文件內(nèi),所以一張合法的 JPEG 圖片能夠同時(shí)包含合法的 PHP 腳本。
不要信任用戶(hù)。不要信任瀏覽器。構(gòu)建似乎所有人都在提交病毒的后端。
當(dāng)然,也不必害怕,這其實(shí)比看起來(lái)的簡(jiǎn)單。只要記住 “不要信任用戶(hù)” 以及 “有功能解決此問(wèn)題” 便可。
上傳文件往往在處理不當(dāng)?shù)那闆r下,確實(shí)會(huì)帶來(lái)比較大的安全風(fēng)險(xiǎn),特別是用戶(hù)偽造信息,通過(guò)一些特殊請(qǐng)求讓上傳php可執(zhí)行文件,從而引發(fā)網(wǎng)站的安全問(wèn)題。
那么如何有效的防止用戶(hù)上傳php可執(zhí)行文件呢?筆者摘錄了該問(wèn)題的解決方法,供大家參考:
為了解決這個(gè)問(wèn)題,PHP中內(nèi)置了一些特殊函數(shù)方法,只是為了在這種情況下使用。
basename()
第一個(gè)解決方案?---?basename() 它從路徑結(jié)束時(shí)提取路徑的一部分,直到它遇到第一個(gè)斜杠,但忽略字符串末尾的斜杠,參見(jiàn)示例。無(wú)論如何,你會(huì)收到一個(gè)安全的文件名。如果你覺(jué)得安全 - 那么是的這很安全。如果它被不法上傳利用 - 你可以使用它來(lái)校驗(yàn)文件名是否安全。
realpath()
另一個(gè)解決方案?---?realpath()它將上傳文件路徑轉(zhuǎn)換規(guī)范化的絕對(duì)路徑名,從根開(kāi)始,并且根本不包含任何不安全因素。它甚至?xí)⒎?hào)鏈接轉(zhuǎn)換為此符號(hào)鏈接指向的路徑。
因此,您可以使用這兩個(gè)函數(shù)來(lái)檢查上傳文件的路徑。要檢查這個(gè)文件路徑到底是否真正屬于此文件夾路徑。
函數(shù)代碼
編寫(xiě)了一個(gè)函數(shù)來(lái)提供如上的檢查。我并不是專(zhuān)家,所以風(fēng)險(xiǎn)請(qǐng)自行承擔(dān)。代碼如下。
<?php
/**
* Example for the article at medium.com
* Created by Igor Data.
* User: igordata
* Date: 2017-01-23
* @link https://medium.com/@igordata/php-running-jpg-as-php-or-how-to-prevent-execution-of-user-uploaded-files-6ff021897389 Read the article
*/
/**
* 檢查某個(gè)路徑是否在指定文件夾內(nèi)。若為真,返回此路徑,否則返回 false。
* @param String $path 被檢查的路徑
* @param String $folder 文件夾的路徑,$path 必須在此文件夾內(nèi)
* @return bool|string 失敗返回 false,成功返回 $path
*
*/
function checkPathIsInFolder($path, $folder) {
if ($path === '' OR $path === null OR $path === false OR $folder === '' OR $folder === null OR $folder === false) {
/* 不能使用 empty() 因?yàn)橛锌赡芟?"0" 這樣的字符串也是有效的路徑 */
return false;
}
$folderRealpath = realpath($folder);
$pathRealpath = realpath($path);
if ($pathRealpath === false OR $folderRealpath === false) {
// Some of paths is empty
return false;
}
$folderRealpath = rtrim($folderRealpath, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
$pathRealpath = rtrim($pathRealpath, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
if (strlen($pathRealpath) < strlen($folderRealpath)) {
// 文件路徑比文件夾路徑短,那么這個(gè)文件不可能在此文件夾內(nèi)。
return false;
}
if (substr($pathRealpath, 0, strlen($folderRealpath)) !== $folderRealpath) {
// 文件夾的路徑不等于它必須位于的文件夾的路徑。
return false;
}
// OK
return $path;
}
結(jié)語(yǔ)
必須過(guò)濾用戶(hù)輸入,文件名也屬于用戶(hù)輸入,所以一定要檢查文件名。記得使用 basename() 。
必須檢查你想存放用戶(hù)文件的路徑,永遠(yuǎn)不要將這個(gè)路徑和應(yīng)用目錄混合在一起。文件路徑必須由某個(gè)文件夾的字符串路徑,以及 basename($filename) 組成。文件被寫(xiě)入之前,一定要檢查最終組成的文件路徑。
在你引用某個(gè)文件前,必須檢查路徑,并且是嚴(yán)格檢查。
記得使用一些特殊的函數(shù),因?yàn)槟憧赡懿⒉涣私饽承┤觞c(diǎn)或漏洞。
并且,很明顯,這與文件后綴或 mime-type 無(wú)關(guān)。JPEG 允許字符串存在于文件內(nèi),所以一張合法的 JPEG 圖片能夠同時(shí)包含合法的 PHP 腳本。
不要信任用戶(hù)。不要信任瀏覽器。構(gòu)建似乎所有人都在提交病毒的后端。
當(dāng)然,也不必害怕,這其實(shí)比看起來(lái)的簡(jiǎn)單。只要記住 “不要信任用戶(hù)” 以及 “有功能解決此問(wèn)題” 便可。