php输入过滤需建立数据流向防御意识:接收用filter_input()校验,输出到html用htmlspecialchars()(三参数),入库用pdo预处理,富文本用htmlpurifier,同一数据按上下文分别处理。

filter_input() 为什么比直接读 $_POST 更安全
直接写 $_POST['email'] 等于把字符串原样交出去,哪怕它带 <script></script> 或 SQL 注释符 --,PHP 也不会拦。而 filter_input() 是「取值即校验」——它不返回原始字符串,只返回通过验证的干净值,或 false。
- 验证整数并限范围:
filter_input(INPUT_POST, 'age', FILTER_VALIDATE_INT, ['options' => ['min_range' => 0, 'max_range' => 120]]),传"18abc"或"200"都得false - 验证邮箱但不清理:
filter_input(INPUT_GET, 'email', FILTER_VALIDATE_EMAIL),合法才返回邮箱字符串,否则false,不改内容 - 别用
FILTER_SANITIZE_STRING:PHP 8.1+ 已废弃,它行为模糊(比如是否编码单引号),容易被绕过
htmlspecialchars() 必须在 echo 前调用,且不能省参数
XSS 不发生在用户提交时,而发生在你把它 echo 到 HTML 页面那一瞬间。哪怕你用 filter_var() 验证过邮箱,只要它要进 <input value="..."> 或 <div>...</div>,就必须过 htmlspecialchars()。
- 必须显式传三个参数:
htmlspecialchars($str, ENT_QUOTES, 'UTF-8')。漏掉第三参数,在旧版 PHP 可能因默认编码不一致导致单引号绕过 -
ENT_QUOTES要带上:否则属性内插值如<input value="<?=$name?>">中的" onclick=alert(1)仍可触发 - 别信模板引擎自动转义:Twig、Blade 默认开启,但可能被
{{ raw }}或{!! !!}关闭;确认每个输出点都覆盖到了
PDO预处理语句是防SQL注入的唯一可靠方式
所有其他方案——addslashes()、mysql_real_escape_string()(已废弃)、甚至 filter_var($id, FILTER_SANITIZE_NUMBER_INT)——都不能替代预处理。因为它们只处理字符串,而数据库真正需要的是「结构分离」。
- 占位符只能是
?或:name,绝不能拼进 SQL 字符串:"WHERE id = " . $_POST['id']是高危写法 - 连接需设错误模式:
new PDO($dsn, $user, $pass, [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]),否则绑定失败可能静默忽略 - 数值型参数不用手动转 int:
$stmt->execute([$_POST['id']])中的字符串"123"会被数据库当作数值处理,前提是字段类型匹配
正则白名单只用于强格式约束,且必须锚定首尾
当业务明确要求「用户名只能是字母数字下划线,3–20位」或「手机号必须是11位纯数字」时,filter_var() 不够用,必须上 preg_match() 白名单。
- 必须用
^和$锚定:preg_match('/^[a-zA-Z0-9_]{3,20}$/', $username),否则"admin<script>"</script>也能匹配开头的admin - 别用
stripos()或str_replace()做敏感词过滤:它们无法处理嵌套、变形、Unicode混淆等绕过手段,真有需求应引入专业库如HTMLPurifier或专用分词引擎 - 富文本内容不能靠
strip_tags()应付:它不校验标签属性(如<a href="javascript:alert(1)"></a>),必须用HTMLPurifier白名单机制
filter_input() 验证长度和字符集,存进数据库;查出来后要显示在 HTML 页面,就得过 htmlspecialchars();如果还要放进 JavaScript 字符串里,就得用 json_encode();放进 URL 参数,就得用 urlencode()。没有万能过滤,只有上下文感知的处理。php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











