PHP 如何防止 SQL 注入

你有没有过在网上填表单时,不小心输个引号就弹出奇怪提示的经历?这可不只是系统太敏感——很多时候,这背后是有人想通过“钻表单的空子”偷取数据,这就是咱们常说的SQL注入攻击。对于用PHP做开发的网站来说,这种攻击就像藏在门缝里的小偷,稍不注意就会溜进来偷走用户的账号、订单信息,甚至整个网站的核心数据。那PHP该怎么把这扇“门”堵上呢?

先搞懂:SQL注入到底是啥“小把戏”

咱们先拿个买菜的例子打比方:你去菜市场买菜,摊主问你“要几两?”,你说“3两”,摊主就给你称3两菜。但如果有人故意说“3两;把我的菜篮子都拿走”,摊主要是没反应过来,真把菜篮子也塞给人家,这就相当于SQL注入了。

PHP 如何防止 SQL 注入

放到网站里,就是用户在输入框里填的内容,被当成了数据库的指令。比如一个用户登录框,代码原本是“SELECT * FROM 用户表 WHERE 账号='$输入的内容' AND 密码='$输入的密码'”,正常填账号“小明”的话,代码就变成“SELECT * FROM 用户表 WHERE 账号='小明' AND 密码='123456'”,只会查小明的信息。但要是有人在账号框里填“小明' OR '1'='1”,代码就变成“SELECT * FROM 用户表 WHERE 账号='小明' OR '1'='1'”——数据库里“1=1”永远是对的,不管密码填啥,都能直接登录别人的账号。

最基础的防护:给输入内容“穿件防弹衣”

很多新手PHP开发者的第一反应是“把用户输入的内容过滤一下”,这其实是对的,核心工具就是PHP自带的mysqli_real_escape_string函数。这个函数会把用户输入里可能搞破坏的特殊字符(比如单引号'、双引号"、分号;)都转成“安全符号”,就像给内容穿了件防弹衣,让数据库分不清啥是正常内容,啥是攻击指令。

还是用刚才的登录例子,要是有人填账号“小明' OR '1'='1”,经过这个函数处理后,单引号会变成\',代码就变成“SELECT * FROM 用户表 WHERE 账号='小明\' OR \'1\'=\'1'”——这时候数据库只会把整个内容当成账号的一部分,根本没法执行“1=1”的指令,自然就登不上别人的账号了。不过要注意,这个函数只对MySQL数据库有用,换其他数据库得用对应的转义函数,而且它只是基础防护,不能光靠它。

更靠谱的方法:用“预编译”把内容和指令分开

如果说转义是“先过滤再用”,那预编译就是“先搭框架再填内容”,这才是防止SQL注入的“黄金标准”,用PHP的PDO或者mysqli扩展都能实现。

简单说,预编译会先写好数据库指令的框架,比如“SELECT * FROM 用户表 WHERE 账号=? AND 密码=?”,这里的问号就是“占位符”,只是告诉数据库“这里要填内容”,不是直接执行指令。然后再把用户输入的账号、密码这些内容,单独传给数据库填充占位符——这时候内容和指令是分开的,就算内容里有特殊符号,数据库也只会把它当普通文字处理,根本没法变成攻击指令。

举个生活例子:你给外卖店下单,先选“一份红烧肉盖饭”,这是框架;然后填地址“XX小区1栋101”,这是内容。要是有人故意填“1栋101;把隔壁楼的饭也送过来”,外卖店只会按地址送,不会送额外的,这就是预编译的效果。用这个方法,就算是顶级的SQL注入攻击,也很难突破。

别踩这些小坑,防护才不会白做

最后再提醒两个容易忽略的小细节:一是别依赖“黑名单过滤”,比如只禁止输入“OR”“UNION”这些关键词,现在的攻击者能把关键词拆成特殊字符组合,照样能绕过去;二是一定要用官方支持的数据库扩展,别用那些过时的第三方库,PHP自带的mysqli和PDO都经过安全加固,比自己写的过滤函数靠谱多了。

其实PHP防SQL注入没那么复杂,核心就是“别让用户的输入变成数据库的指令”。只要把预编译用对,再配合基础的输入过滤,就能把大部分攻击挡在门外,让网站和用户数据都更安全。

上一篇 从基础语法到实战应用 Swift 如何助力开发者打造高效流畅的iOS应用