I'd like to show you my script and would like to hear your security hints. Maybe the code is also useful to someone else.
What I want to archive:
- Someone clicks on a link on my web page
- a htaccess file looks for some file-extensions and if the requested file is one of these, it will be counted.
- After the count (done with PDO prepare), the file is proceed to the user.
Because I want to get maximum security and not offering someone other files than from the upload folder I ask before doing it wrong.
.htaccess:
RewriteEngine on
RewriteRule ^(.*).(rar|zip|pdf|tar|apk|rpm|exe)$ /download.php?file=$1.$2 [L,NC]
download.php
<?php
// Allowed extensions
$filetypes = array("rar", "zip", "pdf", "tar", "apk", "rpm", "exe");
// Details of requested file
$file_path = $_REQUEST['file'];
// NullByte cleaner
$file_path = str_replace(chr(0), '', $file_path);
// Split details
$path_parts = pathinfo($file_path);
$file_name = $path_parts['filename'];
$file_ext = $path_parts['extension'];
// CleanUp
$file_name = preg_replace('/[^\0-9_a-zA-ZäöüÄÖÜ]+/', '', $file_name);
$file_ext = preg_replace('/[^\0-9a-zA-Z]+/', '', $file_ext);
// Create path
$dir_path = $_SERVER['DOCUMENT_ROOT']."/";
$finalpath = $dir_path . $file_name . "." . $file_ext;
// Allowed extension?
if ( in_array($file_ext, $filetypes) ) {
// Does the file exist?
if ( is_file($finalpath) ) {
// Final security check
setlocale(LC_ALL,'en_US.UTF-8');
$local_ext = pathinfo($finalpath, PATHINFO_EXTENSION);
// Allowed local extension?
if ( in_array($local_ext, $filetypes) ) {
// Logging
// ..........
// Download
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename=' . basename($finalpath));
header('Content-Transfer-Encoding: binary');
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Pragma: public');
header('Content-Length: ' . filesize($finalpath));
ob_clean();
flush();
readfile($finalpath);
exit;
}
}
}
// Something went wrong
echo "Error...";
exit;