
嘻道奇闻
- 文章199742
- 阅读14625734
手把手教你用预处理语句防止PHP数据库注入攻击
奇闻2025-05-27 20:57:06
你写的登录功能正在被黑客当后门用?
前两天有个学员问我:"老师,我照着网上的教程写了登录功能,怎么老是有陌生账号登进来?" 一查代码差点没背过气去——他在SQL语句里直接拼接用户输入,活脱脱给黑客开了个VIP通道。今天咱们就来把这个漏洞焊死,保证你看完就能写出铁桶般的安全代码!
第一步:认识你的敌人——SQL注入攻击长啥样?
(打开记事本写个反面教材)
php复制// 这是自杀式写法!千万别学! $username = $_POST['username']; $password = md5($_POST['password']); $sql = "SELECT * FROM users WHERE username='$username' AND password='$password'";
假设用户输入用户名是' OR 1=1 --
,这句SQL就会变成:
sql复制SELECT * FROM users WHERE username='' OR 1=1 -- ' AND password='...'
(敲黑板)看见没?--
后面的内容全被注释掉了,1=1
永远成立,黑客直接登录管理员账号!
第二步:PDO预处理语句——给SQL穿防弹衣
(掏出Visual Studio Code开始演示)
??正确姿势四部曲:??
- ??连数据库要像对待初恋??
php复制$db = new PDO('mysql:host=localhost;dbname=test;charset=utf8', 'user', 'pass'); $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); // 错误必须抛出!
- ??写SQL留空位??
php复制$sql = "SELECT * FROM users WHERE username = :user AND password = :pass";
- ??准备语句就像套模板??
php复制$stmt = $db->prepare($sql);
- ??绑定参数要像安检员??
php复制$stmt->execute([ ':user' => $_POST['username'], ':pass' => md5($_POST['password']) ]);
(突然拍大腿)对了!参数绑定支持各种数据类型,数字、字符串自动处理,再也不用手动加引号了!
第三步:实战演练——注册功能防护指南
假设要写用户注册功能,看我怎么用预处理套三层防护:
php复制try { // 过滤层 $username = trim(htmlspecialchars($_POST['username'])); $age = intval($_POST['age']); // 预处理层 $stmt = $db->prepare("INSERT INTO users (username, age) VALUES (?, ?)"); $stmt->bindParam(1, $username, PDO::PARAM_STR); $stmt->bindParam(2, $age, PDO::PARAM_INT); // 执行层 $stmt->execute(); } catch (PDOException $e) { // 错误处理要友好,别暴露数据库信息 die("哎哟,出错了!错误代码:".$e->getCode()); }
??三个必须注意的坑:??
- 表名和字段名不能用占位符(所以要用白名单校验)
bindParam
和bindValue
的区别(前者绑定变量,后者绑定值)- 记得指定参数类型(比如PDO::PARAM_INT防字符串注入)
第四步:突发情况处理——当预处理语句也不管用时
去年遇到个奇葩案例:有个老系统非得用动态表名,像$table = 'order_'.date('Ym');
这种。这时候怎么办?
??应急方案:??
- 白名单校验法
php复制$allowedTables = ['order_202308', 'order_202309']; if (!in_array($table, $allowedTables)) { die("表哥,别乱改表名啊!"); }
- 严格正则验证
php复制if (!preg_match('/^order_\d{6}$/', $table)) { die("你这表名不对劲啊!"); }
(突然压低声音)说真的,这种情况能避免就避免,实在不行记得用框架的查询构造器!
个人观点:安全防护不是选修课
干了十多年开发,见过太多"先上线再说"的惨剧。2019年某电商平台数据泄露,根源就是个没做预处理的查询语句,直接导致700万用户信息被扒。现在PHP8.2的PDO已经支持更安全的参数绑定,但还有很多人在用mysql_connect这种上古函数。
最后给新人一句忠告:当你觉得"这样写应该没问题"的时候,先假设用户会输入' OR 1=1 --
,多问自己"这代码能防住使坏的人吗?"。记住,每个未处理的用户输入都是定时炸弹,而预处理语句就是最好的拆弹工具!