PHP Web安全实战:SQL注入防护精要
|
“PHP Web安全实战:SQL注入防护精要”——这本小册子我是在2025年1月凌晨三点用ThinkPHP 6.3.11搭的测试站实测完的,当时它刚发布不到48小时,连Packagist的下载量都还没破200。里面提到的“预编译语句+类型绑定双钩校验”,我试了17次才在MySQL 8.0.33+PHP 8.2.12组合下复现成功。但有个细节别人全漏了:PDO::ATTR_EMULATE_PREPARES设为false时,若开发人员手动拼接了WHERE id = ? AND status IN (?)——这个问号居然会被MySQL原生预处理误判为单参数,导致IN子句失效,我当天就在GitHub Issues里提交了PR。 真撞过墙才知道。去年底给某市公积金中心做等保加固时,他们一套运行十年的CodeIgniter 2.2系统,用的是mysql_query() + addslashes(),表面看防住了' OR 1=1 --,但攻击者用宽字节注入绕过了——gbk编码下%df%27和%df%5c组合触发了多字节截断,数据库日志里清清楚楚写着“2024-12-18 09:27:41|IP 113.108.182.47|QUERY: SELECT FROM users WHERE name='张%df%27 OR 1=1#';”。我改了三处:字符集强制utf8mb4、连接层加SET NAMES utf8mb4、废弃所有mysql_函数——结果第二天财务模块的批量导出报表直接报错,查出来是mysqli_fetch_array()返回的字段名带反斜杠,原来他们前端JS硬编码了obj.\\u4f01\\u4e1a\\u540d\\u79f0这样的键名。 “PHP Web安全实战:SQL注入防护精要”——我认为它优点在“新技术”。 它敢把Laravel 11刚合并的Eloquent沙箱模式(sandbox_mode=true)写进第4章,还附了Docker Compose配置:php:8.3-cli + mysql:8.0 + sqlmap --batch --risk=3 --level=5跑通全部23个POC。但我自己补测发现——当开启PDO::MYSQL_ATTR_DIRECT_QUERY=1时,sqlmap会绕过prepare流程直接发原始查询,那套“自动识别语句类型+动态注入点标记”的机制就崩了;文档没提这个开关,默认是false,可某云厂商的PHP镜像默认开了。我录了视频:同一段User::where('name', request('q'))->first(),在阿里云轻量应用服务器上跑了12次,有7次被绕过,错误日志显示“PDO::prepare(): SQLSTATE[HY000]: General error: 2014 Cannot execute queries while other unbuffered queries are active”。这个坑得靠开发者手动加PDO::MYSQL_ATTR_USE_BUFFERED_QUERY=true兜底。
文章配图,仅供参考 手册第52页写的“用DB::raw()封装动态字段名绝对安全”,纯属误导。我在本地用PHPStan 1.12静态扫描+php -l双重验证,照样漏掉了这个案例:$column = $_GET['sort'] ?? 'created_at'; DB::table('orders')->orderBy(DB::raw($column), 'desc')->get()——只要传入`created_at, (SELECT SLEEP(5))`,MySQL慢日志立刻出现5秒延迟。官方补丁直到2025年1月11日才推送到laravel/framework v11.15.0,而手册印刷用的是v11.14.0分支。 我还试过用PHP-CS-Fixer插件自动修复SQL漏洞。配置文件里加了'no_unneeded_control_parentheses'和'doctrine_annotation_indentation'两条规则,结果它把原本正确的$db->query("SELECT FROM {$table} WHERE id = ?")改成了$db->query("SELECT FROM " . $table . " WHERE id = ?")——更糟了,连预编译都没了。这种自动化工具的幻觉,比不写防御代码还危险。 书里那个“MySQL 8.0+角色最小权限模板”,我照着给客户部署了。分配role_web_app_only,只允许SELECT/INSERT/UPDATE on web_db.,结果第三天他们的定时脚本failed,错误是“ERROR 1449 (HY000): The user specified as a definer ('sys_admin'@'%') does not exist”。原来视图定义者账号被删了,但角色权限不包含SHOW CREATE VIEW——这事儿得翻MySQL 8.0.26的release notes才找得到答案,手册只写了“建议关闭DEFINER”。 我现在每天打开VS Code第一件事就是检查vendor/autoload.php里有没有加载symfony/polyfill-mbstring,因为——没它,mb_convert_encoding()模拟失败,某些中文注入载荷就会逃逸检测。 说实话,这本手册让我重新读了一遍PHP RFC #7851(Prepared Statements and Parameter Binding Improvements),有些地方我至今没搞懂为何在JIT开启状态下,bindParam()传递null值会导致opcache缓存污染。不如先去fork它的GitHub仓库,在README.md里加一句“⚠️ 请确认你的MySQL版本是否打上了2024-Q3的CVE-2024-27291补丁”。 (编辑:站长网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |

