防SQL注入、XSS、CSRF三类漏洞的核心措施分别是:用prepare()预处理防SQL注入,所有动态输出HTML前无条件调用htmlspecialchars()防XSS,CSRF Token必须绑定会话且一次性校验防CSRF;其他实践均为这三条的加固或延伸。

直接说结论:防这三类漏洞,核心就三条——prepare() 拦 SQL、htmlspecialchars() 拦 XSS、$_SESSION['csrf_token'] 拦 CSRF;其他所谓“15种实践”多数是围绕这三条打补丁,或在不同环节做加固。
用 prepare() + execute() 替代字符串拼接
这是防 SQL 注入唯一靠谱的路径。只要用户输入进了 SQL 查询,就必须走预处理,没例外。
-
mysql_query()、mysqli_query()直接拼接$_POST或$_GET的写法,哪怕加了addslashes()或mysqli_real_escape_string(),也属于高危代码 - PDO 默认开启模拟预处理(
PDO::ATTR_EMULATE_PREPARES = true),此时占位符会被 PHP 层“假装”解析,绕过数据库真正的参数绑定;必须显式关掉:$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false) - 命名参数(
:name)比问号(?)更易维护,但注意bindValue()和bindParam()区别:前者传值,后者传引用;循环中用bindValue()更安全 - 批量插入别图省事写一个大
INSERT拼 N 个(?, ?)—— 行数多时建议分批,避免参数超限或执行超时
输出前无条件调用 htmlspecialchars()
不是“需要时才转义”,而是所有动态内容插入 HTML 的位置,都得过这一关。漏一处,XSS 就可能爆发。
- 第二个参数必须显式传
ENT_QUOTES | ENT_HTML5,否则单引号不转义,<img src="https://img.php.cn/" alt="PHP安全开发:防范XSS、CSRF、SQL注入的15种最佳实践">这类 payload 仍可执行 - 第三个参数必须是
'UTF-8',否则在非 UTF-8 字符集下可能截断失败,导致编码绕过 - 不要对整段 HTML 做
htmlspecialchars()后再echo—— 这等于把标签当纯文本展示;富文本必须用HTMLPurifier等专用库清洗,而非简单转义 - URL 参数里的用户数据,用
urlencode()(查询参数)或rawurlencode()(路径部分),别用htmlspecialchars()
CSRF Token 必须绑定会话且一次性验证
只在表单里放个隐藏字段远远不够。Token 不和当前会话强绑定,等于没设防。
立即学习“PHP免费学习笔记(深入)”;
- 生成要用
random_bytes(32),别用md5(time().rand())这类可预测值;存进$_SESSION,别存在 cookie 或前端 localStorage - 每次敏感操作(如 POST 修改密码、DELETE 订单)都需校验;校验必须用
hash_equals(),防止时序攻击 - Token 提交后立即失效(尤其涉及资金/权限变更的操作),避免重放;但普通表单可支持多开窗口,Token 不必“一次一换”,可用“会话级+时间戳”组合降低冲突
- API 接口若用 JWT,CSRF 防护逻辑要移到中间件层,不能依赖 session —— 此时需配合
SameSite=StrictCookie 和双提交 Cookie 模式
真正容易被忽略的,是这三件事的交叉点:比如用 prepare() 防了 SQL 注入,但把查出来的用户名直接 echo $_user['name'] 输出到页面,XSS 就来了;又比如 Token 校验写了,但没配 session_start() 或 session 被覆盖,整个 CSRF 防御形同虚设。防护不是堆砌手段,而是让每个环节严丝合缝咬住上一个环节的输出。



















