必须用预处理语句(pdo或mysqli)防止sql注入,addslashes()无效且危险;表名、字段名等需白名单校验;输出过滤(如htmlspecialchars)不防sql注入。

PHP里别用 addslashes() 做SQL转义
它不能防SQL注入,只做简单反斜杠插入,对多字节编码、宽字符、不同字符集下的绕过完全无效。MySQL 5.7+ 默认 strict mode 下甚至可能直接报错。
-
addslashes()不识别当前连接的字符集,mysql_real_escape_string()已废弃且依赖过时扩展 - 它不处理引号外的上下文(比如数字型参数、ORDER BY 子句、表名),误用等于白防
- 如果数据库连接用了
gbk或big5,攻击者可用 %A1%AA 这类双字节构造截断,绕过addslashes()
必须用预处理语句(PDO 或 MySQLi)
这是唯一被广泛验证、语言层和驱动层共同保障的安全方案。核心不是“转义字符串”,而是把数据和SQL结构彻底分离。
- PDO 示例:
$stmt = $pdo->prepare("SELECT * FROM users WHERE id = ? AND status = ?"); $stmt->execute([$id, $status]); - MySQLi 示例:
$stmt = $mysqli->prepare("INSERT INTO log (msg) VALUES (?)"); $stmt->bind_param("s", $msg); $stmt->execute(); - 占位符只支持值(
?或命名参数如:name),不能用于表名、字段名、ORDER BY 字段——这些必须走白名单校验 - 开启
PDO::ATTR_EMULATE_PREPARES = false,否则 PDO 会在客户端模拟预处理,失去服务端解析保护
真要拼接 SQL(极少数场景),用 mysqli_real_escape_string() 而非全局函数
仅限你明确控制了连接字符集、且确认无法用预处理的遗留代码。它依赖当前 mysqli 实例的连接状态,比 addslashes() 可靠,但仍是次选。
- 必须先建立连接,再调用:
$escaped = mysqli_real_escape_string($mysqli, $input); - 不能写成
mysqli_real_escape_string($input)(缺连接资源,会警告并返回false) - 如果连接字符集没设对(比如 PHP 连接时没执行
SET NAMES utf8mb4),它依然可能失效 - 注意:它不处理数字型参数的类型校验,传入
"1 OR 1=1"给 int 字段仍可能被绕过,所以仍需配合(int)强转或filter_var($x, FILTER_VALIDATE_INT)
过滤输出和过滤输入是两回事,别混
防SQL注入只管“进数据库前”;htmlspecialchars() 是防XSS,用在输出到HTML时,对SQL毫无作用。
立即学习“PHP免费学习笔记(深入)”;
- 常见错误:对用户输入先
htmlspecialchars()再插进SQL——这会让单引号变成',存进库的是 HTML 实体,不是原始字符 - 更糟的是:从库读出后又
htmlspecialchars()一次,导致显示为乱码 - 记住:入库用预处理,出库按用途处理——HTML 输出用
htmlspecialchars(),JSON 输出用json_encode(),CSV 用fputcsv()
事情说清了就结束。真正难的不是写对那几行 prepare,而是所有动态拼接点——包括 ORDER BY、LIMIT、表名——都得有对应校验逻辑,而这类地方最容易被漏掉。











