Как показывает практика — загрузка файлов (и в частности изображений) без надлежащего контроля приводит к образованию уязвимостей. В этой публикации рассмотрим практическую реализацию одного из вариантов безопасной загрузки изображений на сервер. Исходная постановка задачи предполагает возможность загрузки пользователем изображения на сервер и возможность дальнейшего его просмотра.
Для начала кратко основные шаги:
— производим отправку изображения с использованием XMLHttpRequest;
— проводим проверку загруженных данных на сервере на предмет «действительно ли это изображение»;
— проводим принудительное преобразование изображения в jpeg
В качестве места хранения изображений был выбран субдомен сайта (images.ваш_сайт.com), который физически отделен от сервера самого сайта. Данный субдомен используется только для загрузки, хранения (ну и показа соответственно) изображений. Таким образом предполагается что даже в случае если невзирая на все предыдущие пункты таки будет загружен какой то «нехороший» файл, то ничего критичного с функционалом самого ваш_сайт.com он сделать не сможет.
Итак перейдем к практической части.
<script type="text/javascript">
function xhr_send( e , d) {
if (d) {
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function(){
if(xhr.readyState == 4){
var backjson=JSON.parse(xhr.responseText);
if (backjson.success == 'ok') {document.getElementById(e).innerHTML = 'Файл загружен успешно';location.reload();}
if (backjson.success == 'false') {document.getElementById(e).innerHTML = backjson.reason;}
}
}
xhr.open("POST", "images.ваш_сайт.com/upload.php");
xhr.setRequestHeader("Cache-Control", "no-cache");
xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
xhr.setRequestHeader("X-File-Name", d.name);
xhr.send(d);
}
}
function xhr_parse(f, e) {
if (f) {
document.getElementById(e).innerHTML = "Выбран файл : " + f.name + "(" + f.type + ", размер:" + f.size + ")";
} else {
document.getElementById(e).innerHTML = "No file selected!";
}
}
function xhr_get(c) {
var xhr_file = null;
if (window.File && window.FileList) {xhr_file = document.getElementById(c).files[0]; xhr_parse(xhr_file, "xhr_status"); }
}
function xhr_submit(e) {
var form = document.getElementById(e);
form.onsubmit = function(){ return false;}
xhr_form = new FormData(form);
xhr_send("xhr_status", xhr_form);
}
</script>
<form id="fload" enctype="multipart/form-data" method="post"/>
<input type="file" size="24" name="xhr_img" onchange="xhr_get(this.id);" id="xhr_input" value="Выбрать"/>
<input type="submit" name="Submit" value="Загрузить" id="xhr_upload" onclick="xhr_submit('fload');"/>
</form>
<div id="xhr_status"></div>
Шаг второй — проводим проверку загруженного файла на сервере. Так как запрос у нас кроссдоменный (даже не взирая на то что мы используем свой собственный субдомен), то для того чтобы Ваш браузер мог получить и обработать ответ от скрипта upload.php — в самом начале добавляем следующие строки:
header('Access-Control-Allow-Origin: http://ваш_сайт.com');
header('Access-Control-Allow-Headers: Content-Type,Cache-Control,X-Requested-With,X-File-Name');
Для проведения проверок и необходимых операций с самим изображением используем class.upload.php
include('class.upload.php');
header('Access-Control-Allow-Origin: http://ваш_сайт.com');
header('Access-Control-Allow-Headers: Content-Type,Cache-Control,X-Requested-With,X-File-Name');
$dir_dest = 'swap';
$dir_pics = $dir_dest;
if (isset($_FILES['xhr_img'])) {
$handle = new Upload($_FILES['xhr_img'], 'ru_RU'); //
if ($handle->uploaded) {
$handle->allowed = array('image/*');
$handle->mime_check = true;
$handle->file_new_name_ext = 'jpg';
$handle->Process($dir_dest);
if ($handle->processed) {
$message = array('success'=> 'ok');
echo json_encode($message);
} else {
$message = array('success'=> 'false', 'reason'=>$handle->error);
echo json_encode($message);
}
$handle-> Clean();
} else {
$message = array('success'=> 'false', 'reason'=>$handle->error);
echo json_encode($message);
}
}
И напоследок я добавил еще конвертацию загруженного изображения в формат jpeg, применение интерлейсинга и установка качества сжатия в 90:
$handle->image_convert = 'jpg';
$handle->jpeg_quality = 90;
$handle->image_interlace = true;
На этом спасибо за внимание и надеюсь кому то пригодится.
Автор: semaster