Improperly Controlled Modification of Dynamically-Determined Object Attributes
The product receives input from an upstream component that specifies multiple attributes, properties, or fields that are to be initialized or updated in an object, but it does not properly control which attributes can be modified.
If the object contains attributes that are not intended to be modified, then an attacker can use the vulnerability to overwrite critical application values, gain privileges, or bypass security checks.
Cómo corregir esta vulnerabilidad
Estrategias de prevención para Mass Assignment basadas en 5 reglas de detección de Shoulder.
Use ModelForm with explicit fields whitelist instead of **kwargs or exclude
- from django.http import JsonResponse - from .models import User - - def update_user(request, user_id): - user = User.objects.get(id=user_id) - for key, value in request.POST.items(): - setattr(user, key, value) - user.save() + from django import forms + from django.http import JsonResponse + from .models import User + + class UserForm(forms.ModelForm): + class Meta: + model = User + fields = ['username', 'email', 'bio'] + + def update_user(request, user_id): + user = User.objects.get(id=user_id) + form = UserForm(request.POST, instance=user) + if form.is_valid(): + form.save() return JsonResponse({'status': 'updated'})
Whitelist allowed attributes before using setattr() or __dict__.update()
- from flask import request - - @app.route('/update', methods=['POST']) - def update(): - user = User.query.get(1) - data = request.get_json() - for key, value in data.items(): + from flask import request, abort + + ALLOWED_ATTRS = {"username", "email", "display_name"} + + @app.route('/update', methods=['POST']) + def update(): + user = User.query.get(1) + data = request.get_json() + for key, value in data.items(): + if key not in ALLOWED_ATTRS: + abort(400, f"Cannot update field: {key}") setattr(user, key, value) db.session.commit() return 'Updated'
Use explicit field lists in serializers and mark privilege fields as read-only
from rest_framework import serializers from django.contrib.auth.models import User class UserSerializer(serializers.ModelSerializer): class Meta: model = User - fields = '__all__' + fields = ['id', 'username', 'email', 'first_name', 'last_name'] + read_only_fields = ['id']
Validate input with Zod schema and use explicit field assignment instead of spreading req.body
import { PrismaClient } from '@prisma/client'; - const prisma = new PrismaClient(); - - app.post('/api/users', async (req, res) => { - const user = await prisma.user.create({ - data: { ...req.body } - }); - res.json(user); - }); - - // Attacker sends: { "email": "[email protected]", "role": "admin", "credits": 99999 } + import { z } from 'zod'; + const prisma = new PrismaClient(); + + const createUserSchema = z.object({ + email: z.string().email(), + name: z.string().min(1).max(100), + }); + + app.post('/api/users', async (req, res) => { + const input = createUserSchema.parse(req.body); + const user = await prisma.user.create({ + data: { + email: input.email, + name: input.name, + role: 'user', // Set server-side, not from input + } + }); + res.json(user); + });
Use explicit field assignment or class-transformer with excludeExtraneousValues instead of spreading req.body
import { getRepository } from 'typeorm'; - import { User } from './user.entity'; - - app.put('/api/users/:id', async (req, res) => { - const repo = getRepository(User); - const user = await repo.findOne(req.params.id); - Object.assign(user, req.body); - await repo.save(user); - res.json(user); - }); - // Attacker sends: { "email": "[email protected]", "role": "admin", "isAdmin": true } + import { plainToClass } from 'class-transformer'; + import { validate } from 'class-validator'; + import { UpdateUserDto } from './update-user.dto'; + + app.put('/api/users/:id', async (req, res) => { + const dto = plainToClass(UpdateUserDto, req.body, { + excludeExtraneousValues: true, + }); + const errors = await validate(dto); + if (errors.length > 0) return res.status(400).json({ errors }); + + const repo = getRepository(User); + const user = await repo.findOne(req.params.id); + user.email = dto.email; + user.username = dto.username; + await repo.save(user); + res.json(user); + });
Encuentra vulnerabilidades en tu código
Usa Shoulder para escanear tu código en busca de patrones Improperly Controlled Modification of Dynamically-Determined Object Attributes. 5 reglas.
# Scan with Shoulder CLI npx @shoulderdev/cli trust --cwe=915 # Or scan entire project npx @shoulderdev/cli trust .
Reglas de Detección (5)
Qué buscar en las revisiones de código
Estos patrones indican vulnerabilidades potenciales de Improperly Controlled Modification of Dynamically-Determined Object Attributes. Búscalos durante las revisiones de código y auditorías de seguridad.
Escanea tu base de código para Improperly Controlled Modification of Dynamically-Determined Object Attributes
Shoulder CLI encuentra patrones vulnerables en toda tu base de código.