跳到内容

大批量赋值备忘单

简介

定义

软件框架有时允许开发者自动将 HTTP 请求参数绑定到程序代码变量或对象中,以方便开发者使用该框架。这有时会造成损害。

攻击者有时可以利用这种方法创建开发者从未预期的参数,进而创建或覆盖程序代码中未预期的变量或对象。

这被称为大批量赋值漏洞。

别名

根据所涉及的语言/框架,此漏洞可以有几个别名

  • 大批量赋值 (Mass Assignment): Ruby on Rails, NodeJS。
  • 自动绑定 (Autobinding): Spring MVC, ASP NET MVC。
  • 对象注入 (Object injection): PHP。

示例

假设有一个用于编辑用户账户信息的表单

<form>
     <input name="userid" type="text">
     <input name="password" type="text">
     <input name="email" text="text">
     <input type="submit">
</form>  

这是表单绑定的对象

public class User {
   private String userid;
   private String password;
   private String email;
   private boolean isAdmin;

   //Getters & Setters
}

这是处理请求的控制器

@RequestMapping(value = "/addUser", method = RequestMethod.POST)
public String submit(User user) {
   userService.add(user);
   return "successPage";
}

这是典型的请求

POST /addUser
...
userid=bobbytables&password=hashedpass&[email protected]

这是我们设置类 User 实例的 isAdmin 属性值的攻击方式

POST /addUser
...
userid=bobbytables&password=hashedpass&[email protected]&isAdmin=true

可利用性

此功能在以下情况下变得可利用:

  • 攻击者可以猜测常见的敏感字段。
  • 攻击者可以访问源代码并检查模型中的敏感字段。
  • 并且具有敏感字段的对象具有一个空构造函数。

GitHub 案例研究

2012年,GitHub 因大批量赋值而被攻击。一名用户能够将其公钥上传到任何组织,从而在他们的仓库中进行任何后续更改。GitHub 的博客文章

解决方案

  • 允许列表(allow-list)可绑定、非敏感的字段。
  • 阻止列表(block-list)不可绑定、敏感的字段。
  • 使用数据传输对象(DTOs)。

通用解决方案

一种架构方法是创建数据传输对象(DTOs),并避免将输入直接绑定到领域对象。DTO 中只包含用户可编辑的字段。

public class UserRegistrationFormDTO {
 private String userid;
 private String password;
 private String email;

 //NOTE: isAdmin field is not present

 //Getters & Setters
}

语言和框架特定解决方案

Spring MVC

允许列表(Allow-listing)

@Controller
public class UserController
{
    @InitBinder
    public void initBinder(WebDataBinder binder, WebRequest request)
    {
        binder.setAllowedFields(["userid","password","email"]);
    }
...
}

查看此处的文档。

阻止列表(Block-listing)

@Controller
public class UserController
{
   @InitBinder
   public void initBinder(WebDataBinder binder, WebRequest request)
   {
      binder.setDisallowedFields(["isAdmin"]);
   }
...
}

查看此处的文档。

NodeJS + Mongoose

允许列表(Allow-listing)

var UserSchema = new mongoose.Schema({
    userid: String,
    password: String,
    email : String,
    isAdmin : Boolean,
});

UserSchema.statics = {
    User.userCreateSafeFields: ['userid', 'password', 'email']
};

var User = mongoose.model('User', UserSchema);

_ = require('underscore');
var user = new User(_.pick(req.body, User.userCreateSafeFields));

查看此处的文档。

阻止列表(Block-listing)

var massAssign = require('mongoose-mass-assign');

var UserSchema = new mongoose.Schema({
    userid: String,
    password: String,
    email : String,
    isAdmin : { type: Boolean, protect: true, default: false }
});

UserSchema.plugin(massAssign);

var User = mongoose.model('User', UserSchema);

/** Static method, useful for creation **/
var user = User.massAssign(req.body);

/** Instance method, useful for updating**/
var user = new User;
user.massAssign(req.body);

/** Static massUpdate method **/
var input = { userid: 'bhelx', isAdmin: 'true' };
User.update({ '_id': someId }, { $set: User.massUpdate(input) }, console.log);

查看此处的文档。

Ruby On Rails

查看此处的文档。

Django

查看此处的文档。

ASP NET

查看此处的文档。

PHP Laravel + Eloquent

允许列表(Allow-listing)

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    private $userid;
    private $password;
    private $email;
    private $isAdmin;

    protected $fillable = array('userid','password','email');
}

查看此处的文档。

阻止列表(Block-listing)

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    private $userid;
    private $password;
    private $email;
    private $isAdmin;

    protected $guarded = array('isAdmin');
}

查看此处的文档。

Grails

查看此处的文档。

Play

查看此处的文档。

Jackson (JSON 对象映射器)

查看此处此处的文档。

GSON (JSON 对象映射器)

查看此处此处的文档。

JSON-Lib (JSON 对象映射器)

查看此处的文档。

Flexjson (JSON 对象映射器)

查看此处的文档。

参考资料和延伸阅读