校验需结合执行上下文:SQL 动态标识符用白名单,条件参数化;JSON 需检查错误码、深度限制与大整数处理;XML 要捕获 libxml 错误并禁用外部实体。

SQL 输入合法性校验:别只靠正则,先看执行上下文
SQL 注入不是靠一个 preg_match 就能拦住的。真正有效的校验必须绑定具体使用场景——是拼进 WHERE 子句的字符串?还是作为表名动态传入?前者可用参数化绕过校验,后者根本不能参数化,必须白名单控制。
- 动态表名、列名、排序字段(
ORDER BY后)必须用预定义枚举或严格白名单,比如只允许['id', 'created_at', 'status'] - 用户输入用于
WHERE条件时,优先走参数化查询(PDO::prepare或mysqli->prepare),而不是“校验后拼接” - 真要校验原始 SQL 片段(如管理后台的即席查询),用现成解析器比正则靠谱:PHP 可用
sql-parser库做语法树分析,检查是否含UNION、SELECT嵌套、子查询等高风险结构 - 常见错误:用
stripslashes+addslashes当防护,这在多字节编码或宽字符场景下形同虚设
if (!in_array($sort_field, ['name', 'email', 'updated_at'], true)) {throw new InvalidArgumentException('Invalid sort field'); }
JSON 结构校验:用 json_decode 的返回值判断远不够
json_decode($input, true) 返回 null 只说明解析失败,但不告诉你为什么失败——是编码问题?还是超深嵌套?还是循环引用(虽然 JSON 本身不允许)?更关键的是,它完全不验证业务结构。
- 先确保
json_last_error()是JSON_ERROR_NONE,再检查json_last_error_msg(),有些错误(如JSON_ERROR_DEPTH)会被静默吞掉 - 结构校验别手写
isset()套娃,用webmozart/assert或原生filter_var($data, FILTER_VALIDATE_REGEXP, [……])配合 JSON Schema(推荐justinrainbow/json-schema) - 注意
json_decode默认把数字全转成 float,大整数(如微信 openid、雪花 ID)会丢失精度;加第二个参数true和第三个参数JSON_BIGINT_AS_STRING才安全 - 常见错误:收到空字符串或空白符就直接
json_decode,结果返回null却没检查json_last_error(),误判为合法 JSON
json_decode($input, true, 512, JSON_BIGINT_AS_STRING) —— 深度 512 是防爆栈,JSON_BIGINT_AS_STRING 防 ID 截断
XML 结构校验:DOMDocument load 失败不等于格式非法
DOMDocument::loadXML() 报错可能是 DTD 不可达、外部实体开启、编码声明错位,甚至只是某处多了个 & 符号。它不区分“格式错误”和“加载失败”。
- 加载前先用
libxml_use_internal_errors(true)拦住警告,再调libxml_get_errors()拿具体错误码,比如XML_ERR_UNCLOSED_TOKEN才是真语法错,XML_ERR_NAME_REQUIRED可能只是开头缺 XML 声明 - 业务级结构校验靠 XSD:用
DOMDocument::schemaValidate(),但注意 PHP 的 libxml 对 XSD 1.1 支持有限,别用assertions类特性 - 禁用外部实体(防 XXE)必须显式调
libxml_disable_entity_loader(true)(PHP < 8.0)或设LIBXML_NONET | LIBXML_NOENT标志,否则校验过程本身就有风险 - 常见错误:用
simplexml_load_string()替代 DOMDocument,它对错误容忍过高,<foo>&</foo>这种会静默丢掉&后所有内容
libxml_disable_entity_loader(true) 必须在 DOMDocument::loadXML() 前调,晚了没用
校验从来不是加一层 if 就完事。SQL 的上下文、JSON 的精度陷阱、XML 的实体加载时机——每个点卡住的位置都不同,漏掉任意一个,表面看着正常,实际早被绕过了。