Как Вы все уже хорошо знаете, самый надежный способ защиты при заливке файлов, это:
1. Переименовывать файлы в имена и расширения файлов независимо от входящего имени файла.
2. Отключить в папке, куда заливаются файлы, выполнение скриптов.
Этот вариант отлично подходит для большинства случаев, когда заливаемый файл нужно использовать на сайте (например: аватар пользователя) и он требует записи в БД для привязки. Реализуется легко и об этом уже на Хабре есть статьи.
Мы же с Вами рассмотрим ситуацию, когда нужно сделать безопасную заливку файлов, без использования БД, с сохранением оригинальных имен и с возможностью заливать что угодно (без каких либо ограничений) с последующей возможностью скачивания данных файлов по URL.
Для лучшего восприятия сейчас реализуем примитивный файлообменник. Для этого нам понадобится: папка, назовем ее, например «files», и 2 скрипта «upload.php» и «get.php».
В скрипте «upload.php» реализуем заливку файла с преобразованием имени в hex-код. Т.е. например, если пользователь решил залить файл «index.php» то он нормально зальется в папку, но с именем «696e6465782e706870» что полностью безопасно для сервера и не выполнится PHP-обработчиком.
Вот скрипт «upload.php»:
<? $uploaddir = "files/"; function StringToHex($s) { $hex = ""; for ($i=0;$i<strlen($s);$i++) $hex=$hex.dechex(ord($s[$i])); return $hex; } if (isset($_FILES['userfile'])) { // Преобразуем имя файла в hex $new_name = StringToHex(strtolower($_FILES['userfile']['name'])); if (move_uploaded_file($_FILES['userfile']['tmp_name'], $uploaddir . $new_name)) { // Ok... } else { // Error... } } ?> <html> <body> <form enctype="multipart/form-data" method="post"> <input name="userfile" type="file" /><input type="submit" value="Сохранить" /> </form> </body> </html>
Сейчас займемся папкой, куда будут попадать файлы. Первым делом поставим ей права 777 и создадим в ней файл «.htaccess»:
RewriteEngine on RewriteRule (.*) get.php?file=$1 [L,QSA]
Ну и осталось добавить в папку «files» скрипт «get.php», который будет отдавать файлы пользователю, для наглядности я в него добавил возможность просмотра файлов в папке с их реальными именами (ответ для тех читателей, которые спросят типа «а зачем все это было, если можно было имена шифровать в md5» и.т.д.).
<? function StringToHex($s) { $h = ""; for ($i=0;$i<strlen($s);$i++) $h=$h.dechex(ord($s[$i])); return $h; } function HexToString($h) { $s = ""; for ($i=0;$i<strlen($h)-1;$i+=2) $s=$s.chr(hexdec($h[$i].$h[$i+1])); return $s; } if (empty($_GET["file"])) { // Для наглядности (вывод содержимого папки) if ($dh = opendir(".")) while (($file = readdir($dh)) !== false) { if (preg_match("/^[a-z0-9]+$/", $file)) echo '<a href="',HexToString($file),'">',HexToString($file),'</a><br />'; } closedir($dh); exit; } $file_to_user = $_GET["file"]; $file_name = StringToHex(strtolower($_GET["file"])); if (file_exists($file_name)) { // Если имя файла поле преобразования совпало с существующим файлом - отдадим его пользователю на скачивание if (ob_get_level()) ob_end_clean(); header('Content-Description: File Transfer'); header('Content-Type: application/octet-stream'); header('Content-Disposition: attachment; filename='.basename($file_to_user)); header('Content-Transfer-Encoding: binary'); header('Expires: 0'); header('Cache-Control: must-revalidate'); header('Pragma: public'); header('Content-Length: '.filesize($file_name)); readfile($file_name); } ?>
Таким образом, для пользователей сайта всегда будут доступны файлы (на скачивание), залитые в папку по нормальным ссылкам:
http://site.xx/files/img.gif http://site.xx/files/index.php http://site.xx/files/jquery.js http://site.xx/files/readme.txt
В реальности же в папке «files» будут файлы с именами:
696e6465782e706870 696d672e676966 6a71756572792e6a73 726561646d652e747874
Ну, вот собственно и все. Естественно есть куча других вариантов реализации защищенной заливки, особенно красиво все можно реализовать с использованием БД, я же решил предоставить свой метод приема/отдачи файлов в их естественном виде, без запретов на скрипты, и при этом с безопасностью для сайта. Для более высокой защиты (например: от повторных имен файлов, и.т.д.) скрипты нужно улучшать, тут только базовая идея реализована.
ссылка на оригинал статьи http://habrahabr.ru/post/183000/
Добавить комментарий