你有没有想过,一个看似普通的图片上传功能,可能正悄悄给黑客打开后门?很多小型企业官网、个人博客甚至内部管理系统,都用PHP做文件上传功能。表面上只是让用户传个头像、交个资料,但如果处理不当,攻击者就能上传恶意脚本,直接控制服务器。
上传功能不是“传了就行”
很多人觉得,只要把文件存到服务器上就完事了。但问题恰恰出在这一步。如果不对上传的文件做严格限制,用户完全可以把一个写好的PHP木马换个名字,比如叫 myphoto.jpg.php,然后通过表单提交上去。一旦这个文件被服务器执行,后果不堪设想。
常见的漏洞长什么样?
下面这个简化的PHP上传代码就很危险:
<?php
if ($_FILES["file"]["error"] == 0) {
$upload_dir = "uploads/";
move_uploaded_file($_FILES["file"]["tmp_name"],
$upload_dir . $_FILES["file"]["name"]);
}
?>
这段代码完全信任用户传来的文件名和类型,没有任何检查。攻击者可以轻松上传一个包含恶意代码的PHP文件,比如 shell.php,然后访问这个地址,就能执行任意命令,查看服务器文件、窃取数据库密码,甚至把整个网站搞瘫痪。
真正安全的做法是层层设防
光靠前端JavaScript判断文件类型根本没用,因为请求可以伪造。真正的防护必须在服务端完成。
第一步是检查MIME类型,但这还不够,因为也能伪造。更可靠的方法是用 finfo 函数检测文件真实类型:
<?php
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mime = finfo_file($finfo, $_FILES["file"]["tmp_name"]);
if ($mime !== 'image/jpeg' && $mime !== 'image/png') {
die("只允许上传图片文件");
}
finfo_close($finfo);
?>
第二步是重命名文件。不要相信用户传来的文件名,最好用时间戳或随机字符串重新命名,避免覆盖系统文件或利用已知路径进行攻击。
第三步是限制上传目录的执行权限。比如在Apache服务器上,可以在上传目录加个 .htaccess 文件:
php_flag engine off
<Files "*.php">
Deny from all
</Files>
这样即使有人上传了PHP文件,服务器也不会执行它,只能当普通文件下载。
还要注意这些细节
文件大小要限制,防止有人上传超大文件拖垮服务器。同时检查文件扩展名黑名单也不够安全,应该用白名单机制,只允许特定几种格式,比如jpg、png、pdf。
还有一个容易被忽略的点:图像文件也可能藏毒。有些攻击会把恶意代码嵌入图片的EXIF信息里,虽然不会直接执行,但在某些解析场景下可能被利用。所以处理上传的图片时,最好用GD库或ImageMagick重新生成一次,剥离潜在风险数据。
最后,所有上传的文件最好放在Web根目录之外。比如网站在 /var/www/html,上传目录设成 /var/www/uploads,并通过PHP脚本控制访问权限。这样外部无法直接通过URL访问到文件,安全性高得多。