在使用PHP進行開發時,經常遇到檔案上傳的場景。其中會隐藏很多我們平時注意不到的安全問題,我總結了一下,主要有幾個方面:
1、檢查使用者傳來的檔案名,避免 ../etc/passwd 這樣的探測
2、有些應用使用了使用者名做為路徑名,那麼也需要對使用者名進行詳細的檢查
3、有些人提到了檔案上傳的Dos攻擊,這個我覺得主要是依靠網絡層面去解決,禁掉頻繁通路的IP,但是對于僵屍網絡的攻擊,貌似沒什麼好的辦法
下面這篇文章中介紹的内容有些老,因為現在已經不是PHP3和PHP4的那個時代,雖然如此,關于檔案上傳中的安全處理,我們還是需要多加注意。
正常的表單沒有提供檔案上傳的功能,是以在 RFC 1867 中提出了《HTML中基于表單的檔案上傳》這個規範。看下面的列子:
<FORM METHOD="POST" ENCTYPE="multipart/form-data">
<INPUT TYPE="FILE" NAME="hello">
<INPUT TYPE="HIDDEN" NAME="MAX_FILE_SIZE" VALUE="10240">
<INPUT TYPE="SUBMIT">
</FORM>
上面的代碼讓使用者從本地機器選擇一個檔案,當點選送出後,檔案就會被上載到伺服器。這顯然是很有用的功能,但是PHP的響應方式使這項功能變的不安全。當PHP第一次接到這種請求,甚至在它開始解析被調用的PHP代碼之前,它會先接受遠端使用者的檔案,檢查檔案的長度是否超過 “$MAX_FILE_SIZE” 變量定義的值,如果通過這些測試的話,檔案就會被存在本地的一個臨時目錄中。是以,攻擊者可以發送任意檔案給運作PHP的主機,在PHP程式還沒有決定是否接受檔案上載時,檔案已經被存在伺服器上了。這裡我就不讨論利用檔案上載來對伺服器進行DOS攻擊的可能性了。
讓我們考慮一下處理檔案上載的PHP程式,正如我們上面說的,檔案被接收并且存在伺服器上(位置是在配置檔案中指定的,一般是/tmp),擴充名一般是随機的,類似“phpxXuoXG”的形式。PHP程式需要上載檔案的資訊以便處理它,這可以通過兩種方式,一種方式是在PHP 3中已經使用的,另一種是在我們對以前的方法提出安全公告後引入的。
但是,我們可以肯定的說,問題還是存在的,大多數PHP程式還是使用老的方式來處理上載檔案。
PHP設定了四個全局變量來描述上載檔案,比如說上面的例子:
$hello = Filename on local machine (e.g "/tmp/phpxXuoXG")
$hello_size = Size in bytes of file (e.g 1024)
$hello_name = The original name of the file on the remote system (e.g "c:\\temp\\hello.txt")
$hello_type = Mime type of uploaded file (e.g "text/plain")
然後PHP程式開始處理根據“$hello”指定的檔案,問題在于“$hello”不一定是一個PHP設定的變量,任何遠端使用者都可以指定它。如果我們使用下面的方式:
http://vulnhost/vuln.php?hello=/etc/passwd&hello_size=10240&hello_type=text/plain&hello_name=hello.txt
就導緻了下面的PHP全局變量(當然POST方式也可以(甚至是Cookie)):
$hello = "/etc/passwd"
$hello_size = 10240
$hello_type = "text/plain"
$hello_name = "hello.txt"
上面的表單資料正好滿足了PHP程式所期望的變量,但是這時PHP程式不再處理上載的檔案,而是處理“/etc/passwd”(通常會導緻内容暴露)。這種攻擊可以用于暴露任何敏感檔案的内容。
PHP手冊中提到“PHP 遵從大多數伺服器系統中關于檔案和目錄權限的安全機制。這就使管理者可以控制哪些檔案在檔案系統内是可讀的。必須特别注意的是全局的可讀檔案,并確定每一個有權限的使用者對這些檔案的讀取動作都是安全的。” 我們知道,/etc/passwd 是全局可讀的…。
參考資料:
1、PHP中檔案上傳中的安全問題
2、RFC1867 HTML中基于表單的檔案上傳
3、PHP手冊,檔案系統安全
4、PHP安全基礎 表單及URL上傳攻擊