十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

sql预处理

sql预处理 PHP 预处理与 SQL 注入一、 认识 SQL 注入1. 什么是 SQL 注入SQL 注入是指攻击者通过在应用程序的输入参数中拼接恶意的 SQL 片段使得后台执行的 SQL 语句偏离预期从而导致数据泄露、篡改或删除。经典漏洞示例直接拼接变量$id$_GET[id];// 假设攻击者输入: 1 OR 11$sqlSELECT * FROM users WHERE id .$id;// 最终执行的 SQL: SELECT * FROM users WHERE id 1 OR 11// 结果整张表的数据全部被泄露2. 为什么传统的转义不够安全早期人们常用addslashes()或mysql_real_escape_string()来转义特殊字符如单引号。但这种方法依赖开发者的人工判断且在复杂的查询条件如ORDER BY、LIMIT后跟数字时不需要引号中极易被绕过维护成本高且不可靠。二、 预处理的核心理念预处理是现代数据库驱动防范 SQL 注入的终极武器。1. 预处理的执行机制预处理将 SQL 语句的执行分为两步预编译先把 SQL 语句的“骨架”发送给数据库引擎。此时不包含任何用户数据数据库只会对骨架进行语法解析和执行计划优化。传参执行将用户输入的数据作为“参数”发送给数据库。数据库会将这些参数**严格当作纯数据字面量**处理绝不会将其解析为 SQL 指令。2. 形象的比喻传统拼接就像给一张纸条写上“请把[用户输入]放进保险箱”。如果用户输入“一百块钱然后把保险箱炸了”整个动作就变了味。预处理像是一个固定格式的表单“收款人金额”。无论用户在横线上填什么银行系统都只会把它当作“名字”和“数字”绝不会把填写的文字当作指令执行。三、 PDO 预处理实战PDO (PHP Data Objects) 是 PHP 推荐的数据库抽象层支持多种数据库默认使用预处理。1. 连接数据库$host127.0.0.1;$dbtest_db;$userroot;$passpassword;$charsetutf8mb4;$dsnmysql:host$host;dbname$db;charset$charset;$options[PDO::ATTR_ERRMODEPDO::ERRMODE_EXCEPTION,// 开启异常模式PDO::ATTR_DEFAULT_FETCH_MODEPDO::FETCH_ASSOC,// 默认关联数组获取PDO::ATTR_EMULATE_PREPARESfalse,// 关闭模拟预处理强制使用数据库原生预处理极其重要];try{$pdonewPDO($dsn,$user,$pass,$options);}catch(\PDOException$e){thrownew\PDOException($e-getMessage(),(int)$e-getCode());}** 注意**PDO::ATTR_EMULATE_PREPARES false非常关键。如果为 true某些旧版本默认PDO 会在本地进行字符串拼接模拟预处理存在边缘情况下的注入风险。设为 false 则完全交由数据库底层处理。2. 增删改查CRUD示例【插入数据】 - 命名参数法推荐$username$_POST[username];$email$_POST[email];// 1. 准备骨架使用 :name 作为占位符$sqlINSERT INTO users (username, email) VALUES (:username, :email);$stmt$pdo-prepare($sql);// 2. 绑定并执行// execute 接收一个关联数组键名必须与占位符一致$stmt-execute([:username$username,:email$email]);echo新用户ID为: .$pdo-lastInsertId();【查询数据】 - 问号参数法$id$_GET[id];// 1. 准备骨架使用 ? 作为占位符$sqlSELECT * FROM users WHERE id ?;$stmt$pdo-prepare($sql);// 2. 绑定并执行// execute 接收一个索引数组按顺序对应 ?$stmt-execute([$id]);// 3. 获取结果$user$stmt-fetch();print_r($user);【更新数据】 - 使用bindParam绑定变量$id5;$newName李四;$sqlUPDATE users SET username ? WHERE id ?;$stmt$pdo-prepare($sql);// bindParam 绑定的是变量引用第三个参数可指定类型更安全$stmt-bindParam(1,$newName,PDO::PARAM_STR);$stmt-bindParam(2,$id,PDO::PARAM_INT);$stmt-execute();echo影响了 .$stmt-rowCount(). 行;四、 MySQLi 预处理实战MySQLi 是 PHP 针对 MySQL 数据库的专用扩展性能略优于 PDO但仅支持 MySQL。1. 连接数据库$mysqlinewmysqli(127.0.0.1,root,password,test_db);if($mysqli-connect_errno){die(连接失败: .$mysqli-connect_error);}$mysqli-set_charset(utf8mb4);// 设置字符集2. 增删改查CRUD示例注意MySQLi不支持命名参数如:name只支持问号占位符?。【插入数据】$username$_POST[username];$email$_POST[email];$sqlINSERT INTO users (username, email) VALUES (?, ?);$stmt$mysqli-prepare($sql);// bind_param(类型字符串, 变量1, 变量2...)// 类型i int, d double, s string, b blob$stmt-bind_param(ss,$username,$email);if($stmt-execute()){echo插入成功ID: .$mysqli-insert_id;}$stmt-close();【查询数据】$id$_GET[id];$sqlSELECT id, username, email FROM users WHERE id ?;$stmt$mysqli-prepare($sql);// 假设 id 是整数$stmt-bind_param(i,$id);$stmt-execute();// 获取结果集$result$stmt-get_result();while($row$result-fetch_assoc()){print_r($row);}$stmt-close();五、 预处理的“盲区”与进阶场景预处理虽然能防住 99% 的注入但它不能防范所有情况。以下场景需要额外处理1. 不能预处理表名和列名预处理只能用于绑定数据值WHERE、VALUES 后面的值不能用于绑定 SQL 关键字、表名或列名。// 错误示范这是无效的会导致语法错误$sqlSELECT * FROM ? WHERE id ?;$stmt$pdo-prepare($sql);$stmt-execute([users,1]);// 正确做法表名/列名使用白名单校验$allowed_tables[users,admins,guests];$table$_GET[table];if(!in_array($table,$allowed_tables)){die(非法数据表);}$sqlSELECT * FROM{$table}WHERE id ?;$stmt$pdo-prepare($sql);$stmt-execute([1]);2. LIKE 模糊查询的预处理在 LIKE 查询中%和_是通配符。如果用户输入了这些符号虽然不会导致 SQL 注入但会导致查询逻辑异常。需要正确拼接%。$keyword$_GET[keyword];// 正确做法将 % 放在 PHP 侧拼接作为整体参数传入$sqlSELECT * FROM users WHERE username LIKE ?;$stmt$pdo-prepare($sql);$searchTerm%{$keyword}%;$stmt-execute([$searchTerm]);// 如果需要防止通配符滥用可以使用 addcslashes 转义// $keyword addcslashes($keyword, %_);3. IN 子句的预处理预处理的一个占位符?只能代表一个值。如果IN语句中有动态数量的参数需要动态生成占位符。$ids[1,2,3,4];// 来自用户的数组// 动态生成同等数量的占位符 ?,?,?,?$placeholdersimplode(,,array_fill(0,count($ids),?));$sqlSELECT * FROM users WHERE id IN ($placeholders);$stmt$pdo-prepare($sql);// 将数组作为参数传入$stmt-execute($ids);$results$stmt-fetchAll();六、 PDO 与 MySQLi 预处理对比总结特性PDOMySQLi (面向对象)支持数据库12 种仅 MySQL占位符语法?和:name均支持仅支持?参数绑定方式execute(array)或bindParam()bind_param(类型, 变量)类型安全可选指定 (PDO::PARAM_INT等)强制指定获取结果集fetch()/fetchAll()get_result()-fetch_assoc()模拟预处理默认开启(建议关闭)无此概念全为原生
返回列表