PHP表单验证:一次没过滤的输出,XSS漏洞就出来了

PHP表单验证:一次没过滤的输出,XSS漏洞就出来了

很多人学 PHP 表单,前面都挺顺:

写个输入框、接个参数、输出一下结果,页面一跑,看起来啥都正常。

但真到了实战,问题往往不是“页面能不能提交”,而是:

尤其是注册页、留言板、用户资料页这类地方,看上去只是收集点信息,实际上最容易成为攻击入口。

今天这篇文章,就从一个最常见的“注册表单”入手,讲清楚 PHP 表单验证为什么不是装饰,而是安全底线。

顺手也把最常见的 XSS 问题、过滤思路、代码示例和实际运行结果一起讲透。

一、为什么表单验证不是“可有可无”?

网站不是记事本,不是用户想填什么你就收什么。

只要你的表单最终要进入:

那就意味着一个现实:

你接收到的,不一定是正常用户输入,也可能是攻击载荷。

如果不做验证,会遇到几类典型问题:

垃圾数据污染数据库

用户名乱填、邮箱乱写、字段格式全错,数据质量直接报废。恶意脚本注入 用户提交一段脚本,后端又原样输出,页面直接被执行。业务逻辑被绕过 必填项为空、枚举值非法、字段长度异常,都会影响业务。

所以表单验证有两个核心目的:

这两件事,缺一不可。

二、先做一个完整的注册表单:名字、邮箱、爱好、备注、性别

我们先搭一个常见的注册页面,不追求花哨,重点看结构和数据流。

这个表单包括:

前端页面示例




    
    注册页面



    用户名:

E-mail:

爱好: 足球 篮球 羽毛球 看书 写字

备注:


性别: 男 女

页面效果大致如下

这个前端结构不复杂,核心点有几个:

1. 多选框要用数组命名

name="hobby[]"

因为爱好可能选多个,后端接收到的是数组。

2. 单选按钮的 name 必须相同


这样才能保证“二选一”。

3. 文本域用

因为备注通常是多行内容,不适合普通 input。

三、后端接收数据:先别急着验证,先确认能不能拿到

很多人一上来就想写复杂逻辑,结果连值是否接收到都没搞清楚。

正确思路很简单:

先接收、再输出、再验证、最后再加固。

后端接收示例

这里为了演示方便,前后端写在同一个页面。

<?php
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $name   = $_POST['name'] ?? '';
    $email  = $_POST['email'] ?? '';
    $hobby  = $_POST['hobby'] ?? [];
    $remark = $_POST['remark'] ?? '';
    $gender = $_POST['gender'] ?? '';
    echo "用户名:".$name."
"; echo "邮箱:".$email."
"; echo "爱好:"; foreach ($hobby as $item) { echo $item." "; } echo "
"; echo "备注:".$remark."
"; echo "性别:".$gender."
"; } ?>

运行结果示例

假设提交内容如下:

页面输出:

用户名:张三
邮箱:zhangsan@test.com
爱好:足球 看书
备注:喜欢安全研究
性别:男

到这一步,说明最基本的接收流程没有问题。

四、很多人以为“已经成功了”,其实漏洞这时候就已经出现了

如果你现在觉得“能收、能显、功能完成”,那恰恰说明最容易忽略安全问题。

因为这段代码有一个非常典型的隐患:

后端把用户输入原样输出到了页面。

这意味着,如果攻击者提交的不是普通文本,而是一段脚本,会发生什么?

答案是:

页面可能直接执行恶意代码。

这就是最典型的 XSS(跨站脚本攻击)。

五、XSS到底是怎么出现的?看一个最直接的例子

假设用户名输入框里,不填正常名字,而是填下面这段内容:

alert(1)

如果你的后端还是像刚才那样直接输出:

echo "用户名:".$name."
";

那么浏览器在渲染页面时,会把这段内容当成真正的 HTML / JS 代码执行,而不是普通字符串。

漏洞复现代码



    用户名:
    

测试输入

在用户名框里输入:

alert(1)

运行结果

页面弹出一个浏览器提示框:

1

这个弹窗本身没什么杀伤力php 漏洞补丁修复,但它证明了一件事:

攻击者提交的脚本,被你的页面执行了。

这就已经是漏洞成立了。

六、别把弹窗当玩具:XSS真正危险的地方不是 alert(1)

很多人第一次学 XSS,总觉得就是弹个窗,没啥意思。

这是个误区。

alert(1) 只是验证漏洞存在的最小演示。

真实攻击里,XSS通常会被用来做这些事:

所以只要脚本能执行,就不是“小问题”。

七、为什么会有 XSS?本质就一句话

XSS 的本质很简单:

把不可信输入,当成可信 HTML 输出了。

你本来想让用户输入“文字”,

结果用户传来的是“代码”,

后端不加处理直接输出,

浏览器就替你执行了。

问题不在浏览器,

也不在用户输入框,

而在你后端没有做输出过滤。

八、XSS最常见的基础防护:()

在 PHP 里,做 HTML 输出过滤时,最常见的一个函数就是:

htmlspecialchars()

它的作用是把特殊字符转换成 HTML 实体。

例如:

这样一来,浏览器看到的就不再是“可执行标签”,而是普通文本。

最简单的修复示例



    用户名:
    

还是输入同样的测试内容

alert(1)

运行结果

页面不再弹窗,而是直接显示:

用户名:alert(1)

注意PHP表单验证:一次没过滤的输出,XSS漏洞就出来了,这里显示的是字符串,不是执行脚本。

这说明过滤生效了。

九、怎么确认它真的被转义了?可以看页面源代码

加固之后,如果你查看页面源代码,通常会看到类似:

用户名:alert(1)

这说明:

所以不会执行。

十、字符串能这么处理php 漏洞补丁修复,数组呢?很多人就卡在这里了

前面用户名、邮箱、备注、性别都还是单值。

但爱好是数组:

$hobby = $_POST['hobby'] ?? [];

这时候有些人会犯一个错,直接对整个数组做:

htmlspecialchars($hobby)

这通常是不对的。

因为 () 处理的是字符串,不是整个数组。

正确做法:在循环里逐个处理

echo "爱好:";
foreach ($hobby as $item) {
    echo htmlspecialchars($item, ENT_QUOTES, 'UTF-8')." ";
}
echo "
";

这样每个元素都被单独过滤了。

十一、给你一份完整一点的安全输出版代码

下面这份代码php 漏洞补丁修复,把刚才的注册表示例和输出过滤整合到一起。

完整示例

<?php
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $name   = $_POST['name'] ?? '';
    $email  = $_POST['email'] ?? '';
    $hobby  = $_POST['hobby'] ?? [];
    $remark = $_POST['remark'] ?? '';
    $gender = $_POST['gender'] ?? '';
    echo "用户名:".htmlspecialchars($name, ENT_QUOTES, 'UTF-8')."
"; echo "邮箱:".htmlspecialchars($email, ENT_QUOTES, 'UTF-8')."
"; echo "爱好:"; if (is_array($hobby)) { foreach ($hobby as $item) { echo htmlspecialchars($item, ENT_QUOTES, 'UTF-8')." "; } } echo "
"; echo "备注:".htmlspecialchars($remark, ENT_QUOTES, 'UTF-8')."
"; echo "性别:".htmlspecialchars($gender, ENT_QUOTES, 'UTF-8')."
"; } ?> 注册页面 用户名:

E-mail:

爱好: 足球 篮球 羽毛球 看书 写字

备注:


性别: 男 女

正常提交运行结果示例

输入:

输出:

用户名:李四
邮箱:lisi@test.com
爱好:篮球 写字
备注:平时喜欢写代码
性别:女

恶意输入测试结果示例

如果用户名输入:

alert(1)

输出:

用户名:alert(1)

页面不会弹窗。

这就说明最基础的 XSS 输出过滤已经起作用了。

十二、只有输出过滤还不够,表单还要做“验证”

过滤和验证,不是一回事。

很多人容易混成一个概念,其实它们分工不同:

1. 验证

验证关注的是:

2. 过滤

过滤关注的是:

所以一个完整的表单处理流程应该是:

先验证,再处理,再安全输出。

十三、先做几个最基础的后端验证规则

比如我们给这个注册表单加几个常规要求:

验证版代码示例

<?php
$errors = [];
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $name   = trim($_POST['name'] ?? '');
    $email  = trim($_POST['email'] ?? '');
    $hobby  = $_POST['hobby'] ?? [];
    $remark = trim($_POST['remark'] ?? '');
    $gender = $_POST['gender'] ?? '';
    if ($name === '') {
        $errors[] = "用户名不能为空";
    }
    if ($email === '') {
        $errors[] = "邮箱不能为空";
    } elseif (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
        $errors[] = "邮箱格式不正确";
    }
    if (empty($hobby) || !is_array($hobby)) {
        $errors[] = "请至少选择一个爱好";
    }
    if ($gender !== '男' && $gender !== '女') {
        $errors[] = "性别选择无效";
    }
    if (empty($errors)) {
        echo "提交成功
"; echo "用户名:".htmlspecialchars($name, ENT_QUOTES, 'UTF-8')."
"; echo "邮箱:".htmlspecialchars($email, ENT_QUOTES, 'UTF-8')."
"; echo "爱好:"; foreach ($hobby as $item) { echo htmlspecialchars($item, ENT_QUOTES, 'UTF-8')." "; } echo "
"; echo "备注:".htmlspecialchars($remark, ENT_QUOTES, 'UTF-8')."
"; echo "性别:".htmlspecialchars($gender, ENT_QUOTES, 'UTF-8')."
"; } else { foreach ($errors as $err) { echo htmlspecialchars($err, ENT_QUOTES, 'UTF-8')."
"; } } } ?>

运行结果示例1:正常输入

输入:

输出:

提交成功
用户名:testuser
邮箱:test@example.com
爱好:足球
备注:
性别:男

运行结果示例2:邮箱格式错误

输入:

输出:

邮箱格式不正确

运行结果示例3:不选性别

输出:

性别选择无效

十四、前端限制不是安全,后端验证才是底线

前端当然也可以写一些校验,比如:

但这些只能改善用户体验,不能当安全防线。

因为前端规则是可以被绕过的:

所以你在页面上写:

这不代表攻击者就一定会传合法邮箱。

真正有用的,还是后端的判断逻辑。

十五、关于 $:能接不代表该用

很多人为了省事,喜欢写:

$name = $_REQUEST['name'] ?? '';

这样看起来确实方便,GET 和 POST 都能收。

但从安全开发角度来说,最好不要在这种表单处理里滥用 $,原因很简单:

所以正式写法更推荐:

$name = $_POST['name'] ?? '';

前端是 POST,就坚持按 POST 接。

十六、() 也不是万能药,别乱套

很多人学会这个函数之后,容易走到另一个极端:

见到任何数据都无脑套一层。

这也不对。

要明白它的定位:

它主要用于 HTML 输出上下文的转义。

也就是说,当数据最终要输出到网页标签中时,它很合适。

但如果你的数据要进入:

那对应的防护方式并不一样。

所以别把“会用一个过滤函数”理解成“已经全面安全”。

十七、实战里真正容易出问题的,不是技术点,而是“顺手就直接输出了”

很多漏洞不是工程师故意写出来的,

而是因为开发节奏太快,写着写着就变成了这样:

echo $_POST['name'];

或者:

echo $remark;

甚至后台审核页直接把数据库里的内容原样展示出来。

这种代码在业务上看“能跑”,

在安全上看就是隐患。

所以在代码审计时,最值得盯的一类点就是:

尤其输出点,只要没有过滤PHP表单验证:一次没过滤的输出,XSS漏洞就出来了,就很可能是 XSS 的落脚点。

十八、再说一个行业里经常被误解的事:发现漏洞,不等于你一定要亲手改源码

很多刚进安全行业的人,一发现漏洞,第一反应就是:

“那我改代码修掉它。”

这个想法不能说错,但真到了企业环境里,事情没这么简单。

因为直接改客户代码,风险非常高:

特别是老系统、历史系统、第三方采购系统,风险更大。

所以在真实项目里,更常见的做法往往是:

发现漏洞并验证输出漏洞报告给出修复建议由原开发方或客户研发团队修复必要时结合安全产品做外围防护

比如:

这不是“甩锅”,而是职责边界和风险控制。

十九、开发和安全都该记住的表单安全原则

把今天这篇文章压缩成最核心的几条,就是下面这些。

对开发来说对安全测试来说对管理者来说二十、最后总结

PHP 表单验证,真正难的不是把页面写出来,

而是你能不能守住“输入到输出”这条链路。

这条链路里,最容易出问题的就是两件事:

不验证不转义

公告:
1. 本站所有资源来源于用户上传和网络,如有侵权请联系站长!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系站长处理!
6. 本站不售卖代码,资源标价只是站长收集整理的辛苦费!如果您喜欢该程序,请支持正版软件,购买注册,得到更好的正版服务。
7. 站长QQ号码 2205675299

资源库 - 资源分享下载网 » PHP表单验证:一次没过滤的输出,XSS漏洞就出来了

常见问题FAQ

关于资源售价和售后服务的说明?
本站所有资源的标价均为本站收集资源的辛苦费,不代表资源本身的价值。软件是高智慧高价值的商品,不可能是白菜价。本站资源标价只是赞助费用,收取的赞助费仅用来维持本站的日常运营!毕竟收集整理资料需要投入云计算资源和站长大量的精力。
代码有没有售后服务和技术支持?
由于代码的运行具有不可预见性,本站不保证代码完整可运行,不提供技术支持和售后服务。 本站原创代码都是站长自己开发的,可以有偿提供技术支持服务。 网站里标明【亲测】的代码都是站长亲测过的,其他的代码由于精力有限,没有一一测试,不能保证代码就一定能够使用,更没有技术支持服务,下载前请自行斟酌。
有没有搭建服务?
由于搭建服务比较费时费力,所以本站除了原创代码外均不提供搭建服务。本站分享代码纯属兴趣爱好,不以盈利为目的,请勿咨询有没有搭建服务,谢谢理解。
链接地址失效了怎么办?
请带上资源链接地址联系客服,工作时间内我们看到后将第一时间回复。
关于解压密码
本站资源一般都没有加密,如果发现需要解压密码的,那么就输入 hao.35dc.com 试试。

发表评论

资源库,由老程序员细心甄别、精心筛选,只为提供优质的源码资源

关于我们 联系我们