介紹如何透過 PHP 讓使用者下載靜態檔案,但隱藏實際檔案名稱與路徑,保護原始檔案資訊。
隱藏檔案名稱與路徑
假設我們想讓使用者從網頁伺服器上下載靜態的檔案,但是不要讓使用者看到該檔案在伺服器上的路徑與名稱,可以撰寫一個簡單的 PHP 指令稿,將靜態檔案透過 PHP 程式來遞送,屏蔽後方的靜態檔案資訊:
# 原始檔案位置 $filePath = '/secret_data/data1.zip'; # 檔案類型(一般性檔案) $contentType="application/octet-stream"; # 下載後的檔名 $newFileName = 'your_data.zip'; # 各種檔案標頭 header("Pragma: public"); header("Expires: 0"); header("Cache-Control: must-revalidate, post-check=0, pre-check=0"); header("Cache-Control: public"); header("Content-Description: File Transfer"); header("Content-Type: " . $contentType); header("Content-Length: " .(string)(filesize($filePath)) ); header("Content-Transfer-Encoding: binary\n"); # 以附件方式下載檔案,並指定下載後的預設檔名 header('Content-Disposition: attachment; filename="' . $newFileName . '"'); # 輸出檔案內容 readfile($filePath); exit();
這裡我們將靜態檔案移出網頁伺服器的網頁目錄,放在系統上的其他位置,因此該檔案無法直接被下載,然後我們再透過這個 PHP 程式將其內容讀取出來,搭配自訂的標頭資訊一起輸出,這樣使用者下載的檔案內容是相同的,但是看不到檔案名稱與實際路徑,藉以達到保護的目的。
瀏覽器撥放 MP4 影片檔
如果是 MP4 這類的影片檔,希望透過 PHP 下載之後,讓 Google Chrome 等瀏覽器直接以內建的撥放器直接撥放(不要另存檔案),可以將「檔案類型」修改一下,並且以一般的方式下載檔案:
# 原始檔案位置 $filePath = '/secret_data/data1.mp4'; # 檔案類型(MP4 影片檔) $contentType="video/mp4"; # 各種檔案標頭 header("Pragma: public"); header("Expires: 0"); header("Cache-Control: must-revalidate, post-check=0, pre-check=0"); header("Cache-Control: public"); header("Content-Description: File Transfer"); header("Content-Type: " . $contentType); header("Content-Length: " .(string)(filesize($filePath)) ); header("Content-Transfer-Encoding: binary\n"); # 輸出檔案內容 readfile($filePath); exit();
隱藏索引
提供資料下載的網頁,通常都會採用索引的方式來對應下的檔案,例如 https://your.com/down.php?id=12
就是用來下載編號 12
的檔案,但這種方式會暴露檔案的編碼方式,也可讓使用者輕易使用簡單的下載工具大量下載資料。
若要避免暴露檔案編碼索引,可以採用 AES 加密的方式,先將索引進行加密,這樣用於網頁的 URL 中就不會暴露相關的資訊。
首先自行定義 AES 加密與解密的函數:
# AES 加密 function encrypt($key, $payload) { $iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length('aes-256-cbc')); $encrypted = openssl_encrypt($payload, 'aes-256-cbc', $key, 0, $iv); return base64_encode($encrypted . '::' . $iv); } # AES 解密 function decrypt($key, $garble) { list($encrypted_data, $iv) = explode('::', base64_decode($garble), 2); return openssl_decrypt($encrypted_data, 'aes-256-cbc', $key, 0, $iv); }
在網頁中需要建立下載超連結的地方,全部都以 AES 的方式將敏感的資訊加密,例如:
# 原始檔案位置 $filePath = '/secret_data/data1.mp4'; # 加密的下載位置 echo 'https://your.com/down.php?eid=' . urlencode(encrypt("YOUR_PASSWORD", $filePath));
加密過的網址會像這樣:
https://your.com/download.php?eid=MWM2RHcxV1VMOXR2ajZNclZoRGV6TlZ6Z0xZUXdKUWZ3ZlVIb29sRlZEYz06OhUUI2QzmPOAAE6v5GybvA8%3D
然後在 download.php
中取得加密後的檔案資訊,用同樣的密碼解密,取得下載的檔案資訊,最後再以前述的方式產生資料讓使用者下載:
# 解密後的下載位置 $filePath = decrypt("YOUR_PASSWORD", $_GET['eid']);
這樣的方式可讓所有的下載網址都沒有規則可循,基本上也無法直接以簡單的方式大量下載檔案。
參考資料:StackOverflow