PHP Web安全实战:SQL注入防护精要
|
去年秋天,我接手了一个被SQL注入攻击搞崩溃的PHP电商项目——攻击者通过订单参数注入恶意代码,直接清空了3个分库的订单表,损失超过20万。这让我意识到,传统防护手段(比如转义函数、正则过滤)根本不够用——攻击者能用宽字节注入绕过addslashes(),用时间盲注绕过错误回显限制,甚至通过UNION注入直接读取数据库结构。 新技术里,预处理语句(Prepared Statements)是真正的“防注神器”。我测试过PDO和MySQLi两种驱动,发现PDO的参数绑定更灵活——比如用:name占位符时,攻击者传入的单引号会被自动转义为字符串内容,而不是作为SQL语法解析。去年11月,我在一个用户登录接口用PDO重构后,拦截了127次尝试注入的请求(日志显示攻击者用了“admin' OR '1'='1”这类经典payload),而之前用mysql_real_escape_string()时,同样的攻击会导致数据库连接池爆满。 但预处理也不是万能的——动态表名、列名的场景容易翻车。比如用户自定义报表功能,表名可能来自参数,这时候直接拼接到SQL里,预处理也救不了。我试过用白名单过滤表名,但客户要求支持任意表名查询,最后只能用存储过程封装——把表名作为参数传给存储过程,在过程内部做权限校验,虽然麻烦点,但比直接拼接安全得多。 还有个细节容易被忽略:ORM框架的“假安全”。去年有个项目用Eloquent ORM,开发者以为框架会自动防注入,结果被攻击者通过模型属性的__get魔术方法注入成功——Eloquent的where条件拼接时,如果直接用用户输入的字符串(比如$request->input('filter')),照样会生成危险SQL。后来我强制要求所有查询必须用参数绑定,哪怕是多此一举的where('id', '=', $id),也比直接拼接安全。 失败案例更扎心——有个同行用“正则过滤+转义”的组合拳,结果被攻击者用CHAR()函数绕过:把单引号拆成CHAR(39),正则匹配不到,转义函数也不处理数字。这让我彻底放弃正则过滤——除非你能覆盖所有可能的编码方式(十六进制、八进制、Unicode),否则别碰。现在我的原则是:只要涉及数据库操作,必须用预处理或存储过程,其他方法都是自欺欺人。
文章配图,仅供参考 新技术里,我最近在研究Web应用防火墙(WAF)的SQL注入规则库——比如ModSecurity的CRS规则,能拦截90%以上的通用注入攻击。但WAF也有局限——它基于流量特征匹配,遇到慢速注入(每次只发一个字符,延迟几秒)就抓瞎。上个月我测试过,用Python脚本模拟慢速盲注,绕过了某云WAF的防护,直接读出了数据库版本信息——所以,WAF只能是辅助,不能替代代码层防护。下一步我打算研究RASP(运行时应用自我保护)技术——在PHP运行时拦截危险SQL,比如通过扩展hook mysql_query()函数,解析SQL语法树,发现可疑操作直接阻断。不过这技术还新,兼容性是个问题——比如某些旧项目用的PHP 5.6,可能不支持。但总得有人先试,对吧? (编辑:站长网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |

