深入顯出PHP下的檔案上傳

來源:互聯網
上載者:User
深入淺出PHP下的檔案上傳


來源:http://www.360weboy.com/php/upload.html

?

檔案作為一種特殊的表單資料,通過http post請求方式提交至伺服器的時候,php會產生一個$_FILES全域數組,相關的檔案資訊會存放在這個全域數組中。我將在這篇文章中通過一些樣本代 碼來闡述php下的檔案上傳,並且深入看下關於檔案上傳內部的實現機制,最後簡單說下如何加強這方面的安全性!

檔案上傳

為了讓用戶端的使用者能夠上傳檔案,我們必須在使用者介面中提供一個表單用於提交上傳檔案的請求。由於上傳的檔案是一種特殊資料,不同於其它的post資料,所以我們必須給表單設定一個特殊的編碼:

1

<form action="upload.php" method="POST" enctype="multipart/form-data">

?

以上的enctype屬性,你可能不太熟悉,因為這常常會被忽略掉。但是,如果http post請求中既有常規資料,又包含檔案類資料的話,這個屬性就應該顯示加上,這樣可以提高針對各種瀏覽器的相容性。

接下來,我們得向表單中添加一個用於上傳檔案的欄位:

1

<input type="file" name="attachment" />

上述檔案欄位在各種瀏覽器中可能表現會有所不同。對於大多數的瀏覽器,上述欄位都會被渲染成一個文字框加上一個瀏覽按鈕。這樣,使用者既可以自行輸入 檔案的路徑到文字框中,也可以通過瀏覽按鈕從本地硬碟上選擇所要上傳的檔案。但是,在蘋果的Safari中,貌似只能使用瀏覽這種方式。當然,你也可以自 定義這個上傳框的樣式,使它看起來比預設的樣式優雅些。

下面,為了更好的闡述怎麼樣處理檔案上傳,舉一個完整的例子。比如,以下一個表單允許使用者向我的本機伺服器上上傳附件:

1
2
3
4
5

<p>請上傳你的附件:</p>
<form action="upload.php" method="POST" enctype="multipart/form-data">
<input type="file" name="attachment" />
<input type="submit" value="上傳附件" />
</form>

提示:可以通過php.ini中的upload_max_filesize來設定允許上傳檔案的最大值。另外,還有一個 post_max_size也可以用來設定允許上傳的最大表單資料,具體意思就是表單中各種資料之和,所以你也可以通過設定這個欄位來控制上傳檔案的最大 值。但是,注意後者的值必須大於前者,因為前者屬於後者的一部分表單資料。

圖1. 顯示在在firefox中的上傳表單

當這個表單提交的時候,http請求會被發送到upload.php。為了顯示具體哪些資訊可以在upload.php中使用,我在upload.php將其列印出來:

1
2
3
4
5
6



header('Content-Type: text/plain');
print_r($_FILES);

?>

下面來做個實驗,假如我通過以上表單上傳一個本部落格的logo到我的本機伺服器www.360weboy.me/upload.php,看看在upload.php中會輸出什麼資訊:

1
2
3
4
5
6
7
8
9
10
11
12

Array
(
? ? [attachment] => Array
? ? ? ? (
? ? ? ? ? ? [name] => boy.jpg
? ? ? ? ? ? [type] => image/jpeg
? ? ? ? ? ? [tmp_name] => D:\xampp\tmp\php1168.tmp
? ? ? ? ? ? [error] => 0
? ? ? ? ? ? [size] => 11490
? ? ? ? )

)

以上就是檔案上傳後,在全域數組中的關於當前上傳檔案的所有資訊。但是,我們是否能夠保證這些資訊是安全的,假如name或者其它資訊被篡改過了呢?我們時刻需要對來自用戶端的資訊保持警惕!

具體的http請求的各個部分

為了更好的理解檔案上傳,我們必須核對下用戶端發送的http請求中到底包含了那些具體的資訊。先前我上傳的附件是本部落格的logo,因為是圖片,不太適合我們做以上實驗。所以,我重新上傳一個test.text文字檔,其中具體包含了以下內容:

1
2
3
4
5

360weboy

360days

Life Of A Web Boy

Okay。現在我上傳這個文字檔,在upload.php中會輸出:

1
2
3
4
5
6
7
8
9
10
11
12

Array
(
? ? [attachment] => Array
? ? ? ? (
? ? ? ? ? ? [name] => test.txt
? ? ? ? ? ? [type] => text/plain
? ? ? ? ? ? [tmp_name] => D:\xampp\tmp\php51C0.tmp
? ? ? ? ? ? [error] => 0
? ? ? ? ? ? [size] => 40
? ? ? ? )

)

我們再來看下相關的瀏覽器發送的http post請求(一些可選的頭部我省略了):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

POST /upload.php HTTP/1.1
Host: www.360weboy.me
Referer: http://www.360weboy.me/
multipart/form-data; boundary=---------------------------24464570528145
Content-Length: 234
?
-----------------------------24464570528145
Content-Disposition: form-data; name="attachment"; filename="test.txt"
Content-Type: text/plain

360weboy

360days

Life Of A Web Boy
-----------------------------24464570528145--

從上面的請求格式中有幾個欄位我們要關注下的,分別是name, filename以及Content-Type.它們分別表示上傳檔案框在form表單中的欄位名-attachment,使用者從本地硬碟中上傳的檔案名稱 – test.txt,以及上傳的檔案格式 – text/plain(代表文字檔)。然後,我們看到一行空行下面的,就是這個上傳檔案中的具體內容。

安全性的加強

為了加強檔案上傳中的安全性,我們需要檢查下$_FILES全域數組中的tmp_name和size。為了確保tmp_name指向的檔案確實是剛 剛使用者在用戶端上傳的檔案,而不是指向的類似/etc/passwd,可以使用php中的函數is_uploaded_file()來進行下判斷:

1
2
3
4
5
6
7
8
9


?
$filename = $_FILES['attachment']['tmp_name'];
?
if (is_uploaded_file($filename)) {
? ? /* 是一個上傳的檔案. */
}
?
?>

某些情況下,使用者上傳檔案後,可能會將上傳成功的檔案的內容顯示給使用者看下,那麼上述代碼的檢查尤其重要。

另外一個需要檢查的就是上傳檔案的mime-type, 也就是上述upload.php中輸出數組的type欄位。 我在第一個例子中上傳的是一個圖片,所以$_FILES['attachment']['type']的值為’image/jpeg’。 如果打算在伺服器端只接受image/png, image/jpeg, image/gif, image/x-png 以及 image/p-jpeg這些mime-type的圖片,可以用類似下面的代碼了進行檢查(只是舉個例子,具體代碼,比如報錯等,應該遵循你的系統中的機 制):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

$allow_mimes = array(
? ? 'image/png',
? ? 'image/x-png',
? ? 'image/gif',
? ? 'image/jpeg',
? ? 'image/pjpeg'
);

$image = $_FILES['attachment'];

if(!in_array($image['type'], $allow_mimes)) {
? ? die('對不起, 你上傳的檔案格式不準確;我們只接受圖片檔案.');
}

// 繼續處理上傳的圖片檔案

正如你看到的,我們已經保准了檔案的mime-type是符合伺服器端的要求的。但是,這樣是不是就可以防止惡意使用者上傳其它有害檔案,還是不夠 的,因為這個mime-type惡意使用者是可以偽裝的。 比如使用者做了一張jpg圖片,在圖片的中繼資料中寫入了一些惡意的php代碼,然後儲存為尾碼名為php的檔案。當這個惡意檔案上傳的時候,將順利通過服務 器端對於mime-type的檢查,被認為是一張圖片,裡面的危險的php代碼將會被執行。具體的圖片的中繼資料類似如下:

1
2
3
4
5

File name ? ?: image.jpg
File size ? ?: 182007 bytes
File date ? ?: 2012:11:27 7:45:10
Resolution ? : 1197 x 478
Comment ? ? ?:

我們可以看到,在圖片中繼資料的Comment欄位中加入了php代碼。所以,很顯然,為了防止類似危險情況發生,還必須對上傳檔案的副檔名進行一次必要的檢查。下面的代碼對前面的檢查Mime-type的代碼進行了加強:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

$allow_mimes = array(
? ? 'image/png' => '.png',
? ? 'image/x-png' => '.png',
? ? 'image/gif' => '.gif',
? ? 'image/jpeg' => '.jpg',
? ? 'image/pjpeg' => '.jpg'
);

$image = $_FILES['attachment'];

if(!array_key_exists($image['type'], $allow_mimes )) {
? ? die('對不起, 你上傳的檔案格式不準確;我們只接受圖片檔案.');
}

// 擷取略去尾碼名的檔案名稱:
$filename = substr($image['name'], 0, strrpos($image['name'], '.'));

// 添加尾碼名
$filename .= $allow_mimes[$image['type']];

// 繼續處理上傳的檔案

通過上述的代碼,我們確保即使上傳的圖片的元檔案中包含了php代碼的話,圖片檔案會被重名為尾碼名為圖片格式的檔案,所以其中的php代碼也不會被執行了。上述代碼對正常的上傳的圖片也不會有任何負面影響。

進行了上述的幾步提高安全性的檢查步驟後,如果你只是要把上傳的檔案儲存到一個指定的目錄中,那麼就可以使用php的預設函數move_uploaded_file來實現了:

1
2
3
4
5
6
7
8
9
10


?
$tmp_filename = $_FILES['attachment']['tmp_name'];
$filename = '/path/to/attachment.txt';
?
if (move_uploaded_file(tmp_filename, $filename)) {
? ? /* $temp_filename 儲存在臨時目錄中的上傳檔案, 然後成功將其儲存到對應目錄下的attachment.txt檔案中. */
}
?
?>

你也許還要對上傳檔案的大小進行限制,那麼你可以通過filesize函數來擷取上傳檔案的大小,進行判斷後做進一步處理,這具體就不在這將了,自己去折騰吧。

?

  • 聯繫我們

    該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

    如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

    A Free Trial That Lets You Build Big!

    Start building with 50+ products and up to 12 months usage for Elastic Compute Service

    • Sales Support

      1 on 1 presale consultation

    • After-Sales Support

      24/7 Technical Support 6 Free Tickets per Quarter Faster Response

    • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.