C’è una vulnerabilità particolare che non si manifesta come la classica rottura che ci si aspetterebbe: è paradossalmente la continuità che la svela. Questo perché non interrompe il flusso dell’applicazione e non viola il modello: lo asseconda e lo completa.
Il Mass Assignment è una conseguenza logica di ciò che il codice crede di essere, non proviene da alcun attacco esterno.
Nei framework moderni — Rails, Laravel, NestJS — l’oggetto non è più una struttura passiva, è divenuto un’entità aperta, permeabile, modellata dal flusso dei dati. Ed è proprio qui che nasce l’ombra.
L’illusione dell’oggetto “neutro”
In teoria, un oggetto rappresenta uno stato.
In pratica, nei framework moderni, è una promessa di mutabilità.
user.update(req.body)Questa riga non aggiorna solo campi.
Dichiara implicitamente che:
ogni proprietà presente è legittima, intenzionale, autorizzata.
Il Mass Assignment nasce quando il codice smette di chiedersi chi sta parlando e da dove arriva il significato del dato.
L’oggetto non è più un’entità ontologica stabile.
Diventa una superficie semantica.
Questa riga sembra corta, innocua, quasi ovvia. In realtà è una frase completa, con un soggetto, un’azione e una conseguenza e dice “Prendo tutto quello che l’utente mi ha inviato e lo uso per modificare l’utente”: non dice quali cose, o fino a che punto, né con quali limiti, dice solo: tutto.
🧠 Semiologia dell’input
1. Il body come testo interpretabile
Un JSON non è solo una struttura dati. È un discorso.
{
"email": "user@site.com",
"name": "Mario"
}Questo è ciò che l’interfaccia dice ed il backend ascolta tutto ciò che viene pronunciato, anche ciò che non era previsto.
{
"email": "user@site.com",
"name": "Mario",
"role": "admin",
"balance": 100000,
"isBlocked": false
}Il framework non interpreta l’intenzione o il contenuto: interpreta la forma, la sua espressione.
Ed è qui che il linguaggio diventa potere: se una proprietà è sintatticamente valida, è semanticamente accettata.
2. Escalation di privilegi come atto linguistico
Nel Mass Assignment non “si ottiene” un privilegio. Lo si dichiara.
Non c’è exploit, shell, RCE. C’è un’affermazione: “Sono admin.” E il sistema, non avendo criteri per negarla, risponde: “Va bene.”
Questo è il punto filosofico cruciale: il codice non distingue tra descrizione e prescrizione.
Dire role=admin non descrive uno stato, lo produce.
La colpa dell’astrazione
I framework moderni nascono per ridurre il codice esplicito, ma dobbiamo ricordare che ogni riga non scritta è una decisione delegata. Quando un framework fornisce scorciatoie come queste, non sta solo semplificando: sta insegnando un modello del mondo.
Rails:
User.update(params)👉 Ciò significa che “Prendo tutti i dati che mi arrivano dalla richiesta web e li uso per modificare l’utente”.
Cosa vuol dire davvero:
params= tutto ciò che l’utente ha inviato, anche ciò che non vedeupdate= rendi questi dati veri nel database
Il sistema non distingue:
- tra nome e ruolo
- tra profilo e privilegi
- tra ciò che era previsto e ciò che è stato indovinato
📌 In linguaggio umano:
“Se riesci a scriverlo, io lo salvo”.
Laravel:
$user->update($request->all());👉 “Prendo tutto quello che l’utente mi ha mandato e lo copio sull’utente.”
$request->all()= non seleziono, non filtro, non giudicoupdate= accetto come vero
Il framework non chiede:
- “Questo campo era previsto?”
- “Questo campo è legittimo?”
Chiede solo:
- “È scritto bene?”
NestJS:
Object.assign(entity, body);Queste non sono scorciatoie.
Sono atti di fede.
Il programmatore accetta che:
- l’input sia coerente
- l’utente sia onesto
- l’oggetto sappia cosa è giusto per sé
Ma l’oggetto non ha etica.
Ha solo campi.
👉 “Copia ogni proprietà che trovi nei dati ricevuti dentro l’oggetto.”
Per chi non programma:
- non è una copia selettiva
- non è una fusione prudente
- è una sovrapposizione totale
Se nel body compare una parola chiave sensibile,
il sistema la accetta come identità dell’oggetto.
“Ma le best practice esistono”
Qui nasce l’obiezione classica: “Non è colpa del framework. Esistono whitelist, DTO, strong parameters”. Ed è vero. Il punto, però, non è cosa è possibile fare: è cosa è facile fare.
I framework educano attraverso i default, non attraverso la documentazione; i tutorial mostrano scorciatoie, non confini; e i boilerplate raccontano una storia precisa, ovvero “L’input è affidabile fino a prova contraria.”
Il Mass Assignment non nasce dall’ignoranza, è semplicemente un modello implicito:
- l’utente è onesto
- l’input è coerente
- l’oggetto sa cosa è giusto per sé
Ma l’oggetto non ha etica. Ha solo campi.
🔍 L’ombra nei pacchetti
Quando il bug è una feature documentata
Il Mass Assignment è pericoloso perché è legittimo.
È nei tutorial.
È negli esempi ufficiali.
È nei boilerplate.
Quando una vulnerabilità è prevista dal design, smette di essere percepita come tale.
E allora la responsabilità si sposta:
- dal singolo developer
- alla cultura del framework
- alla comunità che accetta il default come neutralità
Ma nessun default è neutro.
Ogni default è una scelta etica congelata nel codice.
⚖️ Etica della mutabilità
1. Fiducia implicita come superficie d’attacco
Il Mass Assignment non sfrutta un bug.
Sfrutta una fiducia non dichiarata.
Il sistema non dice mai:
“Puoi cambiare tutto.”
Ma nemmeno dice:
“Questo no.”
E nell’ambiguità, l’attaccante non mente.
Completa il discorso.
2. White-hat e black-hat attraversano lo stesso varco
Il white-hat invia il campo per dimostrare la falla.
Il black-hat lo invia per restare invisibile.
La differenza non è tecnica.
È intenzionale.
Ma il codice non distingue intenzioni.
Espone possibilità.
3. UX della sicurezza: l’inganno dei campi invisibili
Un campo non mostrato non è un campo protetto.
È solo un campo non dichiarato.
La UI racconta una storia rassicurante.
L’API ne accetta molte altre.
Questa frattura è etica prima che tecnica:
l’utente crede in un mondo, il backend ne consente un altro.
⚙️ La difesa come presa di posizione
Difendersi dal Mass Assignment non significa “mettere una pezza”, è decidere cosa non può diventare vero.
Ogni contromisura efficace non filtra dati: delimita il reale.Nel momento in cui il sistema accetta input, compie una scelta ontologica: stabilisce quali frasi possono modificare il mondo applicativo e quali devono restare senza effetto.
DTO come atto ontologico (NestJS)
class UpdateUserDto {
@IsString()
name: string;
@IsEmail()
email: string;
}👉 “Queste sono le uniche frasi che puoi dire al sistema.”
Il DTO non è solo una struttura tecnica.
È un vocabolario autorizzato.
Whitelist come confine morale (Laravel)
protected $fillable = ['name', 'email'];👉 “Solo questi due aspetti della persona possono essere modificati.”
Tutto ciò che non è nominato:
- non viene ascoltato
- non viene applicato
- non ha effetto sul mondo
📌 Filosoficamente:
ciò che non è detto non può accadere
Campi immutabili come sovranità (Rails)
attr_readonly :role, :is_adminNon sono pattern.
Sono dichiarazioni di potere.
👉 “Questi campi esistono, ma non possono essere cambiati da nessuno.”
Qui il codice introduce un concetto etico forte:
- il potere può esistere
- ma non può essere negoziato via input
Il sistema afferma:
“Non tutto ciò che puoi dire può cambiare la realtà”
💭 Conclusione: l’oggetto non è mai innocente
Il Mass Assignment ci costringe a una verità scomoda:
la mutabilità è potere.
Ogni campo scrivibile è una leva.
Ogni update è una riscrittura del mondo applicativo.
Ogni framework che semplifica, amplifica.
L’hacking, qui, non rompe il sistema.
Lo prende sul serio.
E la domanda finale — quella che Ethics&Code non può evitare — resta aperta:
se il codice accetta tutto ciò che è ben formato…
chi decide cosa ha il diritto di diventare vero?
