PHP安全获取GET/POST参数需先校验存在性、类型和合法性,禁用$_REQUEST;数字用filter_input(INPUT_GET, 'page', FILTER_VALIDATE_INT),邮箱用FILTER_VALIDATE_EMAIL;入库必须用预处理语句防SQL注入,密码须password_hash加密。

PHP 怎么安全地获取 $_GET 或 $_POST 参数
直接用 $_GET['id'] 或 $_POST['name'] 取值,是绝大多数新手第一反应,但也是 SQL 注入、XSS 和空值报错的源头。
必须先判断键是否存在、类型是否符合预期、内容是否合法:
- 永远不用
$_REQUEST—— 它混了 GET/POST/COOKIE,行为不可控,调试时容易绕晕 - 用
filter_input()替代裸访问:filter_input(INPUT_POST, 'email', FILTER_SANITIZE_EMAIL)比trim($_POST['email'])更可靠 - 对数字类参数,优先用
filter_input(INPUT_GET, 'page', FILTER_VALIDATE_INT),失败直接返回false,比intval()少一堆隐式转换陷阱 - 如果参数必填但缺失,
isset()+!empty()组合不够——empty('0')是 true,empty(' ')也是 true,建议用trim($val) === ''显式判断
把参数写进 MySQL 之前,为什么不能拼 SQL 字符串
像 "INSERT INTO user (name) VALUES ('" . $_POST['name'] . "') 这种写法,只要用户输个 O'Reilly 或 1'; DROP TABLE user; --,数据库就凉了。
唯一靠谱的做法是预处理(Prepared Statement):
立即学习“PHP免费学习笔记(深入)”;
- 用
PDO时,必须调用prepare()+execute(),占位符用:name或?,不要手动拼接 - 用
mysqli时,坚持走prepare()→bind_param()→execute()流程,bind_param('s', $name)中的's'类型声明不能省,否则整数可能被当字符串截断 - 别信“我只收数字参数所以不用预处理”——整数也可能触发宽字节注入或类型绕过,统一走预处理最省心
- 如果用的是旧项目还在用
mysql_*函数,请立刻停用,它早在 PHP 7.0 就被移除了,连mysqli_real_escape_string()都不是万能的
INSERT 执行失败时,常见错误信息和对应检查点
看到 SQLSTATE[HY000]: General error: 1364 Field 'xxx' doesn't have a default value 或 Column 'email' cannot be null,不是代码写错了,而是表结构和写入逻辑没对齐。
- 先查建表语句:
SHOW CREATE TABLE user,确认字段是否设了NOT NULL却没给默认值,或设了DEFAULT CURRENT_TIMESTAMP但 PHP 没传该字段 - 如果用了自增主键,但插入时显式传了
id = 0或NULL,MySQL 8.0+ 默认会报错,得改成不传该字段,或确保sql_mode没开STRICT_TRANS_TABLES -
execute()返回false时,别只打个echo 'fail'—— 要调$pdo->errorInfo()或$mysqli->error看真实错误,很多问题出在字符集(比如表是utf8mb4,但连接没设SET NAMES utf8mb4) - 批量插入时,一次传 500 行比循环 500 次单条快得多,但要注意 MySQL 的
max_allowed_packet限制,默认才 4MB,超了就卡在Lost connection to MySQL server during query
实际存库前,哪些校验不能跳过
用户提交的邮箱、手机号、JSON 数据,看着格式对,不代表能直接入库。漏掉这步,后面查数据全是坑。
- 邮箱用
filter_var($email, FILTER_VALIDATE_EMAIL),但注意它不验证域名是否存在,也不拦截test@localhost这类非法内网地址 - 手机号别只用正则匹配 11 位数字——国内要区分 13/14/15/17/18/19 开头,还要考虑带
+86或空格括号的情况,建议用专门库如libphonenumber - 如果是 JSON 字段(比如
config TEXT),入库前必须json_encode($arr, JSON_UNESCAPED_UNICODE),且检查json_last_error() === JSON_ERROR_NONE,否则存进去一堆null - 敏感字段如密码,绝不能明文存——哪怕只是测试环境。至少用
password_hash($pwd, PASSWORD_ARGON2ID),别碰md5()或sha1()
参数到数据库这一路,真正麻烦的从来不是“怎么写”,而是“哪一步没校验、哪一种边界没覆盖”。尤其是空字符串、零值、超长文本、特殊 Unicode 字符,最容易在上线后某天突然崩一条查询。











