预处理语句是防sql注入唯一可靠手段,因其将sql结构与数据彻底分离,数据库先编译模板再传入纯参数,使恶意输入如' or 1=1 --仅作字符串匹配;mysqli_real_escape_string和addslashes因字符集依赖或不识别多字节编码而不可靠。

预处理语句是防 SQL 注入唯一可靠手段,mysqli_real_escape_string 和 addslashes 不能替代它;htmlspecialchars 必须带 ENT_QUOTES 和明确字符集才对 XSS 有效;CSRF 令牌必须绑定会话且一次性使用,否则形同虚设。
为什么 prepare + execute 是 SQL 注入的硬防线
预处理语句把 SQL 结构和数据彻底分离:数据库先编译语句模板,再把参数作为纯数据传入,根本不会去解析参数里的引号、分号或注释符。哪怕用户输入 ' OR 1=1 --,它也只会被当作一个字符串值匹配,不会改变查询逻辑。
常见错误是误以为“用了 mysqli_real_escape_string 就安全了”——但它依赖 MySQL 连接的字符集配置,一旦 SET NAMES 或连接时指定的字符集和实际不符(比如页面用 UTF-8,但连接设成 latin1),就可能绕过转义。而 addslashes 完全不识别字符集,对多字节编码漏洞(如 GBK 双字节截断)毫无防御力。
- 必须用
PDO或MySQLi的原生预处理,ORM 如 Laravel Eloquent 默认启用,但手动写原生 SQL 时别跳过prepare - 命名参数(
:name)比问号(?)更易维护,尤其在多个相同值重复出现时 - 不要在预处理中拼接表名、字段名或
ORDER BY子句——这些无法参数化,需白名单校验
htmlspecialchars 用错参数等于没防 XSS
默认调用 htmlspecialchars($str) 只转义双引号,单引号仍可被用于属性上下文注入,比如:<input value="<?php" echo>> 。当用户传入 ' onfocus=alert(1) autofocus=',就能触发执行。
正确做法是始终显式传入三个参数:htmlspecialchars($str, ENT_QUOTES, 'UTF-8')。其中 ENT_QUOTES 确保单双引号都被转义,'UTF-8' 防止因字符编码不一致导致的绕过(如浏览器误判为 ISO-8859-1)。
- JavaScript 上下文不能用
htmlspecialchars:输出到<script></script>内或内联事件里,必须用json_encode($str, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT) - URL 参数值要用
urlencode(),不是htmlspecialchars - 富文本场景(如文章编辑器)必须用专用库如
HTMLPurifier,而非简单过滤
CSRF 令牌失效的三种典型场景
CSRF 防御本质是证明“这个请求确实来自当前用户的合法操作页面”,而不是第三方诱导发起。令牌一旦设计不当,就会被绕过。
最常踩的坑是令牌未绑定会话或未及时失效:
- 令牌存在 cookie 里但没设
HttpOnly和Secure标志,可能被 XSS 窃取 - 所有表单共用同一个会话级令牌(
$_SESSION['csrf_token']),用户开两个标签页提交,第二个会因令牌已被消耗而失败,导致体验断裂 - 令牌生成后长期有效(比如存 session 里从不更新),攻击者只要拿到一次,就能反复重放
推荐做法:每次表单渲染时生成新令牌(bin2hex(random_bytes(32))),同时存入 session 并关联时间戳;提交时验证令牌存在、未过期(比如 2 小时)、且未被使用过;验证通过后立即从 session 中清除该令牌。
真实项目里最容易被忽略的细节
安全不是加几个函数就完事。比如 filter_var($_POST['id'], FILTER_VALIDATE_INT) 能拦住非数字输入,但如果后续直接拼进 SQL(哪怕只是日志记录语句),依然可能触发二次注入;又比如设置了 Content-Security-Policy 头,但忘了禁用内联样式(style 属性)或 eval(),XSS 仍可能通过其他路径执行。
真正起作用的是组合约束:输入层校验类型、存储层用预处理、输出层按上下文编码、传输层加令牌与 CSP。任何一个环节松动,都可能让整条链路失效。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











