日前跑一個 RESTful 服務舊案以 PHPUnit 設計的測試案例,發現有一些上傳檔案到 RESTful 服務的測試項目總是失敗。檢查程式後,發現問題出在測試案例的 curl 程式碼。
一直以來,用 PHP 的 curl 函數上傳檔案時,只需要啟用 CURLOPT_POST
選項,再於 CURLOPT_POSTFIELDS
的欄位值中,以 @
標示檔案路徑, curl 就會自動幫我們處理讀取檔案內容並上傳給遠端的工作。參考 PHP 透過 HTTP POST 方法上傳資料與檔案給 RESTful 服務 。
但根據 PHP 線上使用手冊的記載,在 PHP 5.5 版之後,基於安全理由關閉了這項功能。要改用函數 curl_file_create()
,參考 PHP Manual: curl_file_create
。原因很簡單,因為 curl 並不能區分你是真的要上傳檔案或是剛好輸入了一個開頭是 @
字元的字串。如果你設計了一個用 curl POST 上傳資料的程式,又允許使用者輸入資料欄位,那麼駭客就可以故意填上 @/etc/passwd
讓 curl 將主機上的帳號清單傳出去。
像下面的程式碼範例,你設計欄位 field1
是一個字串欄位,讓使用者輸入文字,例如 hello world
。若駭客抓住這一點,故意輸入 @/etc/passwd
,那麼 curl 就會認為這是要上傳檔案,而去讀取 /etc/passwd 的內容並將之送出。
為了修正這個安全問題, PHP 5.5 之後關閉了這個上傳檔案的捷徑(有組態項目可以開啟這功能,但基於安全理由,我不建議這麼做)。如果要上傳的表單欄位中包含檔案型態,那麼設計者必須明確地使用 curl_file_create()
函數處理這個資料欄位。當然,設計者有必要檢查檔案路徑是否安全。幸好這項調整工作很容易修改。如下所示:
對於 PHP 5.4 及其以前的舊版用戶,可以參考網友分享的偷懶替代法,在程式碼中加入下列定義。
這個偷懶法就是定義一個 curl_file_create()
,但內容只是回傳一個以 @
標示的字串。在舊版 PHP 中,它仍然會交給 curl 去自動載入檔案並上傳。但在新版 PHP 中,就會調用內建的 curl_file_create()
函數處理檔案的載入工作。
最後,提供一個無視 PHP 版本也不透過 curl 的方法,即下列的 do_post_request()
函數。
不過這方法假設所有表單欄位都是檔案型態。如果你要上傳的表單混雜了一般文字欄位和檔案型態欄位的話,你要自己區分處理。
樂多舊網址: http://blog.roodo.com/rocksaying/archives/35970791.html