批量赋值怎么发生
当框架把请求字段自动绑定到对象属性时(例如 User.update(req.body)),攻击者只要多传一个字段,就能修改本无权限的属性。role、is_admin、balance、tenant_id 这类字段一旦可被客户端写入,普通用户就能把自己变成管理员。防护思路是显式字段白名单或 DTO(数据传输对象)映射:写哪些字段由服务端决定,不由请求体决定。
基本信息
| 触发写法 | 对象整体绑定请求体、自动填充模型属性 |
|---|---|
| 重点保护字段 | role、is_admin、status、balance、owner、tenant_id |
| 防护方式 | 字段白名单、DTO 映射、只读属性 |
| 常见框架机制 | Rails permit、Laravel only/fillable、Spring DTO、Django 显式字段 |
| 覆盖要求 | 同一对象的每个写入入口都要配白名单 |
典型使用场景
- 用户注册:多传 role=admin 直接提权,最经典的场景
- 资料更新:更新接口比注册接口更容易漏配白名单
- 管理后台:编辑表单绑定整个对象,隐藏字段可被篡改
- 开放 API:第三方对接时字段命名有空格或大小写差异,容易绕过简单黑名单
- 数据导入:批量导入接口把每行记录整体映射到模型
风险写法与安全写法
风险写法与安全写法
风险:User.update(req.body) # 全量字段绑定
安全:User.update({ name: req.body.name, email: req.body.email })
# 框架级白名单:
Rails :params.require(:user).permit(:name, :email)
Laravel :$request->only(['name','email'])
Spring :使用 DTO 接收并手动映射到实体
# 注意:黑名单(排除 role)会被字段名变体绕过,白名单才是默认策略典型入口与风险字段
| 入口 | 风险字段 | 后果 |
|---|---|---|
| 注册接口 | role / is_admin | 普通用户提权为管理员 |
| 资料更新 | status / owner / tenant_id | 越权访问他人数据或绕过审批 |
| 订单接口 | amount / discount / price | 篡改金额造成资损 |
| 审批接口 | approver_id / approved | 伪造审批结果 |
| 导入接口 | 整行字段映射 | 绕过所有单点校验 |
权限字段、状态字段、金额字段属于重点保护对象,任何入口都不应接受客户端直传;这些字段只能由服务端在业务逻辑中赋值。
同一对象常有多个写入入口(注册、更新、后台编辑、批量导入、开放 API),只加固其中一个等于没加固;收敛时按『字段 x 入口』做一张矩阵表逐格确认。