必须用预处理语句且完整执行绑定,因字符串拼接会使用户输入直接混入sql结构,导致' or '1'='1等恶意payload被数据库原样执行;动态表名列名等无法参数化部分须白名单校验,数据库账号还需遵循最小权限原则。

必须用预处理语句,且参数绑定不能省略任何一步——拼接即高危,无论输入看起来多“干净”。
为什么字符串拼接 SELECT * FROM users WHERE id = '$id' 一定会出事
只要用户能控制任意一个输入点(URL 参数、表单字段、HTTP Header),攻击者就能注入 ' OR '1'='1、0; DROP TABLE users; 这类 payload。MySQL 不会区分“你是想查 id=1 还是想执行注释后的内容”,它只认最终拼出来的字符串。哪怕你加了 (int)$id 强转,一旦变量是字符串类型(比如来自 $_GET 的原始值)、或后续逻辑里又转回字符串,防护就失效。
常见错误现象包括:
- 登录页输入
admin' --直接绕过密码验证 - 商品列表页传入
1 UNION SELECT username,password FROM mysql.user拿到数据库账号 - 用
mysql_real_escape_string()(已废弃)或手动addslashes()后仍被宽字节注入击穿
PDO::prepare() 和 mysqli_prepare() 怎么用才真正安全
关键不是“用了预处理”,而是是否完整走完“准备 → 绑定 → 执行”三步。中间跳过绑定、改用字符串插值,等于白做。
- 必须用占位符:
?或命名参数如:username,不能写成"WHERE username = '$user'" - 参数必须通过
bindValue()/bindParam()或execute([$user])传入,不能拼进 SQL 字符串 - 数字类型也不能例外:即使
$id是整数,也要走bindValue(':id', $id, PDO::PARAM_INT),别信(int)$id能兜底 - PDO 默认不开启模拟预处理(
PDO::ATTR_EMULATE_PREPARES = false),务必确认该配置为false,否则 MySQL 实际没走服务端预处理,仍可能被绕过
示例(正确):
$stmt = $pdo->prepare("SELECT * FROM orders WHERE user_id = ? AND status = :status");
$stmt->bindValue(1, $uid, PDO::PARAM_INT);
$stmt->bindValue(':status', $status, PDO::PARAM_STR);
$stmt->execute();
为什么最小权限比写对代码还容易被忽略
预处理防不住所有情况:比如动态表名、列名无法参数化;或者某天有人临时加了 UNION SELECT 查询但忘了检查上下文。这时候数据库账号权限就是最后一道墙。
- Web 应用账号绝不能有
DROP、CREATE、FILE、GRANT OPTION权限 - 禁止跨库访问:只给
GRANT SELECT, INSERT ON myapp.users TO 'webapp'@'10.0.2.0/24',不给myapp.*或*.* - 禁用
LOAD DATA INFILE和SELECT ... INTO OUTFILE,防止通过报错注入或文件读取泄露/etc/passwd或源码 - 连接来源限制:账号只允许从应用服务器 IP 段(如
'10.0.2.%')登录,不用'%'
Python 用 pymysql 时的典型陷阱
pymysql 默认启用模拟预处理(autocommit=False 且未显式关掉 cursorclass 配置时),这意味着 prepare() 只在客户端拼 SQL,不发到 MySQL 服务端编译——等同于没防住。
- 必须显式设置
cursorclass=pymysql.cursors.DictCursor并关闭模拟:conn = pymysql.connect(..., cursorclass=DictCursor, autocommit=True) - 参数一律用
%s占位符,不要用 f-string 或.format()插入变量 - 执行时用
cursor.execute("SELECT * FROM t WHERE name = %s", [name]),而不是execute(f"WHERE name = '{name}'") - 批量插入必须用
executemany()+ 参数列表,别用循环里反复execute()拼接
真正危险的不是你不知道怎么写安全代码,而是你写了看似安全的代码,却因配置偏差或权限放得过宽,让一次小疏漏变成全线崩溃。预处理和最小权限必须同时落地,缺一不可。











