<?php$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. ";} else { echo "File uploading failed. ";}?>upload.html
<form name="upload" action="upload1.php" method="POST" ENCTYPE="multipart/formdata">Select the file to upload: <input type="file" name="userfile"><input type="submit" name="upload" value="upload"></form>上述代码未经过任何验证,恶意用户可以上传php文件,代码如下
<?phpif($_FILES["userfile"]["type"] != "image/gif") {//获取Http请求头信息中ContentType 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. ";} else { echo "File uploading failed. ";}?>该方式是通过Http请求头信息进行验证,可通过修改Content-type ==> image/jpg绕过验证,可以通过脚本或BurpSuite、fiddle修改
upload.php
<?php$imageinfo = getimagesize($_FILES["userfile"]["tmp_name"]);if($imageinfo["mime"] != "image/gif" && $imageinfo["mime"] != "image/jpeg") { echo "Sorry, we only accept GIF and JPEG 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. ";} else { echo "File uploading failed. ";}?>可以通过图片添加注释来绕过此验证。
文件扩展名验证
通过黑名单或白名单对文件扩展名进行过滤,如下代码
upload.php
<?php$blacklist = array(".php", ".phtml", ".php3", ".php4");foreach ($blacklist as $item) {if(preg_match("/$item$/i", $_FILES["userfile"]["name"])) { echo "We do not allow uploading PHP files "; 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. ";} else { echo "File uploading failed. ";}?>当黑名单不全,构造特殊文件名可以绕过扩展名验证
<?php$uploaddir = "d:/uploads/";$uploadfile = $uploaddir . basename($_FILES["userfile"]["name"]);if (move_uploaded_file($_FILES["userfile"]["tmp_name"], $uploadfile)) { echo "File is valid, and was successfully uploaded. ";} else { echo "File uploading failed. ";}?>用户不可以直接通过http://localhost/uploads/ 来访问文件,必须通过view.php来访问
<?php$uploaddir = "d:/uploads/";$name = $_GET["name"];readfile($uploaddir.$name);?>查看文件代码未验证文件名,用户可以通过例如http://localhost/view.php?name=..//php/upload.php,查看指定的文件
<?phprequire_once "DB.php";$uploaddir = "D:/uploads/"; $uploadfile = tempnam($uploaddir, "upload_");if (move_uploaded_file($_FILES["userfile"]["tmp_name"], $uploadfile)) { $db =& DB::connect("mysql://username:password@localhost/database"); if(PEAR::isError($db)) { unlink($uploadfile); die "Error connecting to the database"; } $res = $db->query("INSERT INTO uploads SET name=?, original_name=?,mime_type=?", array(basename($uploadfile,basename($_FILES["userfile"]["name"]),$_FILES["userfile"]["type"])); if(PEAR::isError($res)) { unlink($uploadfile); die "Error saving data to the database. The file was not uploaded"; } $id = $db->getOne("SELECT LAST_INSERT_ID() FROM uploads"); echo "File is valid, and was successfully uploaded. You can view it <a href="view.php?id=$id">here</a> ";} else { echo "File uploading failed. ";}?>view.php
<?phprequire_once "DB.php";$uploaddir = "D:/uploads/";$id = $_GET["id"];if(!is_numeric($id)) { die("File id must be numeric");}$db =& DB::connect("mysql://root@localhost/db");if(PEAR::isError($db)) { die("Error connecting to the database");}$file = $db->getRow("SELECT name, mime_type FROM uploads WHERE id=?",array($id), DB_FETCHMODE_ASSOC);if(PEAR::isError($file)) { die("Error fetching data from the database");}if(is_null($file) || count($file)==0) { die("File not found");}header("Content-Type: " . $file["mime_type"]);readfile($uploaddir.$file["name"]);?>上述代码文件名随机更改,文件被存储在web root之外,用户通过id在数据库中查询文件名,读取文件,可以有效的阻止上述漏洞发生