防止 SQL 注入的核心是:永远不要把用户输入直接拼接到 SQL 语句中。MySQL 项目中,最可靠、最推荐的做法是使用预处理语句(Prepared Statements)配合参数化查询,同时辅以输入校验和权限控制。
用 PDO 或 MySQLi 的预处理机制
这是防御 SQL 注入的黄金标准。数据库驱动会自动区分“SQL 结构”和“数据内容”,从根本上杜绝恶意代码执行。
PDO 示例(推荐):$pdo = new PDO("mysql:host=localhost;dbname=test", $user, $pass);<br>
$stmt = $pdo->prepare("SELECT * FROM users WHERE username = ? AND status = ?");<br>
$stmt->execute([$username, $status]); // 自动转义,类型安全
MySQLi 面向对象示例:$mysqli = new mysqli("localhost", $user, $pass, "test");<br>
$stmt = $mysqli->prepare("INSERT INTO logs (ip, action) VALUES (?, ?)");<br>
$stmt->bind_param("ss", $client_ip, $action); // s=string, i=integer<br>
$stmt->execute();
严格过滤和验证用户输入
预处理不能替代业务层校验。对输入做白名单式约束,能提前拦截异常请求,降低攻击面。
数字类字段(如 ID、分页页码):用(int)强转或
filter_var($input, FILTER_VALIDATE_INT)邮箱、URL、手机号等:使用
filter_var()对应过滤器,例如
filter_var($email, FILTER_VALIDATE_EMAIL)用户名、标题等字符串:限制长度、禁止特殊字符(如
/^[a-zA-Z0-9_\x{4e00}-\x{9fa5}]{2,20}$/u),避免正则回溯漏洞
绝不信任前端传来的任何值——包括 hidden input、cookie、header 中的数据
最小权限原则与数据库配置加固
即使某条语句被绕过,限制数据库账户权限也能大幅降低危害。
Web 应用连接 MySQL 时,使用专用账号,只授予必要权限:GRANT SELECT, INSERT, UPDATE ON mydb.users TO 'webapp'@'localhost';
禁用
DROP、
DELETE、
CREATE、
LOAD_FILE等高危权限 关闭 MySQL 的
local_infile(防止利用
LOAD DATA INFILE读取服务器文件) 生产环境禁用错误信息外泄:PHP 中设
display_errors = Off,MySQL 中避免返回详细报错(如
mysqli_report(MYSQLI_REPORT_OFF))
避免已淘汰或不安全的写法
这些方式看似简单,但极易引入风险,务必替换掉:
❌"SELECT * FROM users WHERE id = " . $_GET['id']—— 直接拼接,高危 ❌
mysql_real_escape_string()(已废弃)或
addslashes()—— 无法覆盖所有编码绕过场景,不推荐 ❌ 动态构建表名/字段名(如
"SELECT * FROM " . $_POST['table'])—— 预处理不支持占位符用于标识符,必须用白名单映射 ✅ 正确做法:表名/字段名由程序内定义数组控制,例如
$allowed_tables = ['users', 'orders']; if (!in_array($table, $allowed_tables)) die('Invalid table');
