PHP Web安全实战:16年经验详解SQL注入防护
|
去年秋天,我接手过一个电商平台的漏洞修复项目——用户反馈登录后能看到其他人的订单信息。排查两小时发现,是某个十年前写的PHP模块里,直接拼接了`$_GET['id']`到SQL语句里。那行代码像根刺扎在我眼里——`$sql = "SELECT FROM orders WHERE user_id = ".$_GET['id'];`,攻击者只需在URL里改个`id=1 OR 1=1`,数据库就全吐出来了。这种低级错误,我十六年里见过太多次,但每次看到还是后背发凉。 SQL注入的防护,说到底就两件事:别信用户输入,别自己拼SQL。可为什么总有人踩坑?我见过最离谱的案例是2018年,某金融系统用`addslashes()`转义参数,结果被攻击者用`\x00`截断绕过,直接提走了五十万——那套系统用了十年没更新,开发团队早解散了。现在看,`addslashes()`早该扔进历史垃圾桶,PHP 7.4+的`PDO::prepare()`配合参数绑定才是王道。去年我测过,同样查询,用`PDO`比拼接字符串快15%,安全性更是天壤之别。 新技术带来的防护升级,远不止参数绑定这么简单。比如PHP 8.1的`FILTER_VALIDATE_INT`过滤,能直接把非数字输入变成`false`,比自己写正则靠谱十倍。我上周刚帮一个客户改了代码——他们之前用`is_numeric()`检查ID,结果被`0x41`(ASCII码A)绕过,现在改用`filter_var($id, FILTER_VALIDATE_INT)`,连十六进制攻击都防住了。还有`mysqli_real_escape_string()`,这函数在PHP 5时代是神器,现在?除非你还在用`mysqli`的旧模式,否则根本不该出现在代码里。 防护SQL注入,最容易被忽略的是存储过程和动态表名。去年我遇到个奇葩需求:客户要根据用户角色查询不同表(比如`admin_orders`、`user_orders`)。开发直接拼接表名:`$table = $_SESSION['role'].'_orders';`——这比参数注入更危险,因为表名没法用参数绑定。我的解法是白名单校验:先定义`$allowed_tables = ['admin_orders', 'user_orders']`,再检查`in_array($table, $allowed_tables)`。这招虽然笨,但能挡住99%的攻击,比什么正则都管用。 有人说“老项目没法改”,我不同意。十六年里,我见过最老的项目是2003年的PHP 4代码,用`mysql_`函数(早被弃用),连错误处理都没有。改造时,我先用`mysqli`替换`mysql`,再逐个函数改成`prepare()`+参数绑定,最后用`error_reporting(E_ALL)`把所有潜在问题暴露出来。花了三个月,代码量多了20%,但SQL注入漏洞从每月3个降到0——这买卖值!
文章配图,仅供参考 当然,没有银弹。上个月我测过一个用ORM框架的项目,开发者以为“ORM自动防注入”,结果被`$user->where('name = '.$_GET['name'])`坑惨了——ORM的参数绑定得显式写,像`$user->where('name = ?', $_GET['name'])`才行。这事儿提醒我:新技术再好,不懂原理照样翻车。下次该写篇《ORM防注入的10个坑》,你们觉得有用吗?(编辑:站长网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |

