直接使用$_files'file'拼接sql会导致sql注入,必须用预处理语句绑定所有用户输入,并配合basename()过滤路径、清除空字节,且确保php连接、表结构、文件编码均为utf8mb4。

直接用 $_FILES['xxx']['name'] 插入 SQL 就是危险源头
PHP 上传表单里拿到的 $_FILES['file']['name'] 是用户可控的,可能含单引号、分号、甚至嵌套 SQL 片段。如果拼接进 INSERT INTO files (filename) VALUES ('<code>$_FILES['file']['name']') 这类语句,攻击者传个 test'.(SELECT password FROM users LIMIT 1).'.txt 就可能拖库。
必须用预处理语句,且不能绕过绑定
预处理不是加个 $pdo->prepare() 就万事大吉——关键在参数是否真正走绑定。常见错误包括:
- 把文件名先拼进 SQL 字符串,再 prepare(等于没用)
- 用
mysql_real_escape_string()(已废弃,且对多字节编码或特殊上下文不保险) - 只对数字字段用绑定,却对
filename字段用字符串拼接
正确做法是所有用户输入字段都走 bindValue() 或 bindParam():
$stmt = $pdo->prepare("INSERT INTO uploads (filename, size) VALUES (?, ?)");
$stmt->bindValue(1, $_FILES['file']['name'], PDO::PARAM_STR);
$stmt->bindValue(2, $_FILES['file']['size'], PDO::PARAM_INT);
$stmt->execute();
文件名还需额外过滤:防路径遍历和空字节
SQL 注入只是冰山一角。$_FILES['file']['name'] 还可能含 ../etc/passwd 或 shell.php%00.jpg 这类 payload,影响后续存储或解析逻辑。即使你只存名字不重命名,也得清理:
- 用
basename()剥离路径部分:basename($_FILES['file']['name']) - 用
str_replace("\0", "", $name)清除空字节(PHP 7.4+ 已默认拒绝含 \0 的文件名,但低版本仍需手动处理) - 若业务允许,建议用
pathinfo($name, PATHINFO_FILENAME)+pathinfo($name, PATHINFO_EXTENSION)分离后单独校验扩展名
别忽略字符集与 collation 对 SQL 安全的影响
即使用了预处理,若数据库连接字符集设为 latin1,而客户端传 UTF-8 多字节字符(如某些宽字节注入变种),仍可能绕过过滤。确保三处统一:
- PHP 连接时指定 charset:
new PDO("mysql:host=localhost;dbname=test;charset=utf8mb4", ...) - 数据表字段用
utf8mb4_unicode_ci(不是utf8,后者仅支持 3 字节 UTF-8) - PHP 文件本身保存为 UTF-8 无 BOM
否则,像 %A1%A1' OR 1=1 -- 这类宽字节 payload 在 latin1 下可能被误解析,让预处理失效。
真正的难点不在写几行 prepare(),而在于确认整个链路——从 HTTP 请求头、PHP 输入过滤、PDO 连接参数、到表结构定义——没有一处松动。漏掉任意一环,安全就变成幻觉。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











