Upload trong PHP (khai thác và phòng chống)
Ứng dụng Web hỗ trợ cho phép người sử dụng thực hiện upload file lên server hiện tại có rất nhiều. Ví dụ như upload image(*.gif, *.jpg), *.pdf, *.doc, ... Trong bài này, sẽ trình bày một số lỗi khi lập trình file upload mà kẻ xấu có thể lợi dụng để upload những mã độc lên server. Những ...
Ứng dụng Web hỗ trợ cho phép người sử dụng thực hiện upload file lên server hiện tại có rất nhiều. Ví dụ như upload image(*.gif, *.jpg), *.pdf, *.doc, ... Trong bài này, sẽ trình bày một số lỗi khi lập trình file upload mà kẻ xấu có thể lợi dụng để upload những mã độc lên server. Những phương thức khai thác mà tôi trình bày chỉ mang tính tham khảo không ủng hộ các bạn khai thác đối với những server bị mắc lỗi.
I. KHAI THÁC LỖI UPLOAD FILE
1) Trường hợp sử dụng JavaScript để kiểm tra file upload
Giả sử ta có kịch bản gồm 2 file như sau:
Trích:
<html>
<title>Secure file upload PHP Web Applications</title>
<head>
<script language=javascript>
function chkFileExtension()
{
var str=document.upload.userfile.value;
var ext=str.substring(str.length,str.length-3);
if ( ext != "gif")
{
alert("File is invalid");
return false;
}
else
{
return true;
};
}
</script>
</head>
<body>
<form name="upload" action="upload1.php" method="POST" ENCTYPE="multipart/form-data" onSubmit="return chkFileExtension()">
Select the file to upload: <input type="file" name="userfile">
<input type="submit" name="upload" value="upload">
</form>
</body>
</html>
Nội dung file Upload1.php
Trích:
<?php
$uploaddir = 'uploads/';
$uploadfile = $uploaddir . basename($_FILES['userfile']['name']);
if (move_uploaded_file($_FILES['userfile']['tmp_name'], $uploadfile)) {
echo "File was successfully uploaded.n";
} else {
echo "File uploading failed.n";
}
?>
Ví dụ trên cho thấy người lập trình kiểm tra phần mở rộng file cho phép file upload bằng cách sử dụng một đoạn mã Javascript trong file upload1.html. Chỉ chấp nhận những file có phần mở rộng là *.gif thì mới được phép upload, cách này một kẻ tấn công dễ dàng vượt qua bằng cách sử dụng một intercepting proxy hoặc có thể viết một đoạn mã bằng Perl, Python,... mà đệ trình trực tiếp đến file upload1.php với những tham số cần thiết.
Trong demo sau tôi sử dụng Burp Suite là một tool rất mạnh khi thực hiện Web Application. Có thể download tại :
Download
Trích:
http://portswigger.net/
2) Trường hợp không sử dụng JavaScript để kiểm tra mà thực hiện kiểm tra trên server với đoạn mã sau:
Trích:
<?php
if($_FILES['userfile']['type'] != "image/gif") {
echo "Sorry, we only allow uploading GIF images";
exit;
}
$uploaddir = 'uploads/';
$uploadfile = $uploaddir . basename($_FILES['userfile']['name']);
if (move_uploaded_file($_FILES['userfile']['tmp_name'], $uploadfile)) {
echo "File is valid, and was successfully uploaded.n";
} else {
echo "File uploading failed.n";
}
?>
Với đoạn mã trên người lập trình hy vọng ngăn chặn được kẻ tấn công upload file độc hại lên server bằng cách sử dụng kiểm tra type=image/gif. Tức là kiểu file cho phép upload ở đây chỉ có thể là file gif. Điều này cũng không an toàn bởi vì kẻ tấn công có thể thay đổi kiểu type=image/gif cho phù hợp với sự kiểm tra của đoạn mã trên nhưng nội dung vẫn chứa mã độc.
3) Trường hợp kiểm tra mime - kiểm tra phần nội dung của file upload với đoạn mã sau:
Trích:
<?php
$imageinfo = getimagesize($_FILES['userfile']['tmp_name']);
if($imageinfo['mime'] != 'image/gif' && $imageinfo['mime'] != 'image/jpeg') {
echo "Sorry, we only accept GIF and JPEG imagesn";
exit;
}
$uploaddir = 'uploads/';
$uploadfile = $uploaddir . basename($_FILES['userfile']['name']);
if (move_uploaded_file($_FILES['userfile']['tmp_name'], $uploadfile)) {
echo "File is valid, and was successfully uploaded.n";
} else {
echo "File uploading failed.n";
}
?>
Như vậy liệu có cải thiện hơn ? An toàn hơn ? Trả lời: có cái thiện hơn nhưng vẫn chưa an toàn. Mặc dù với đoạn mã trên kẻ tấn công không thực hiện thành công như ở Lab2, khi thay đổi type=image/gif vì lúc này người lập trình đã kiểm tra nội dung file upload có đúng là gif hay không ?. Nhưng nếu kẻ tấn công sử dụng một file gif và chèn mã độc vào thì thế nào ? Khai thác sẽ thành công !!!!
4) Trường hợp kiểm tra phần mở rộng của file trên server với đoạn mã sau:
Trích:
<?php
$blacklist = array(".php", ".phtml");
foreach ($blacklist as $item) {
if(preg_match("/$item$/i", $_FILES['userfile']['name'])) {
echo "We do not allow uploading PHP filesn";
exit;
}
}
$uploaddir = 'uploads/';
$uploadfile = $uploaddir . basename($_FILES['userfile']['name']);
if (move_uploaded_file($_FILES['userfile']['tmp_name'], $uploadfile)) {
echo "File is valid, and was successfully uploaded.n";
} else {
echo "File uploading failed.n";
}
?>
Như vậy với đoạn mã người lập trình đã ngăn chặn kẻ tấn công không thể upload file *.php lên server. Tuy nhiên trường hợp này cũng chưa an toàn với những server mà có cấu hình file *.gif, *.jpg cho phép xử lý PHP. Do vậy kẻ tấn công vẫn upload file gif, jpg lên server nhưng có chèn thêm mã độc vào.
III. CÁCH PHÒNG CHỐNG
Mấu chốt giải quyết vấn để làm thế nào an toàn trong file upload đó là lưu giữ file upload lên một nơi mà không thể truy cập bởi user thông qua url. Điều này có thể thực hiện được bằng cách lưu file upload bên ngoài thưc mục webroot hoặc trên web server nhưng từ chối truy cập đến thư mục đó.