Use case: Base44, la porta è chiusa. I dati sono fuori.
Broken Access Control su tre app Base44: Entity API senza autenticazione, mass assignment del ruolo e consumo LLM anonimo.

Cos’è Base44, in poche parole
Base44 è una piattaforma no-code basata sull’AI: descrivi l’applicazione che vuoi costruire e il servizio ne genera interfaccia e logica, con backend, autenticazione e hosting integrati. Puoi continuare a modificarla conversando con l’AI, senza dover assemblare da zero tutta l’infrastruttura.
Per capire l’attrattiva basta pensare a un gestionale interno: attività, documenti, utenti, dashboard. Quello che prima richiedeva di coordinare diversi componenti diventa un’applicazione con cui puoi interagire quasi subito.
È proprio questo passaggio che mi interessa: quando l’app comincia a funzionare, quanto sappiamo di ciò che succede dietro le schermate? Nelle mie analisi su Base44 ho confrontato ciò che l’interfaccia prometteva con ciò che le API permettevano davvero.
Il problema: il login c’era, il confine di sicurezza no
La schermata di login era al suo posto. Il backend restituiva comunque i dati.
Nelle applicazioni che ho esaminato, il login custom gestiva l’accesso alla dashboard. Le Entity API, cioè gli endpoint con cui leggere e scrivere i dati dell’applicazione, offrivano però un percorso separato. In quel percorso ho potuto effettuare operazioni senza presentare né la sessione custom né un token nativo della piattaforma.
Non ho avuto bisogno di violare una password. Ho semplicemente richiesto al backend gli stessi dati che l’interfaccia faceva sembrare riservati.
Una schermata di login, una dashboard riservata e funzionalità apparentemente accessibili soltanto agli utenti autorizzati.
Interrogare direttamente le API, ottenere dati e creare record senza completare il percorso di autenticazione della dashboard.
Un App ID presente nel codice pubblico identifica l’applicazione. Non è una credenziale. Nel momento in cui conoscere quell’identificativo basta a raggiungere dati riservati, il problema è già oltre la schermata di login.
Per raccontarlo ho selezionato tre applicazioni create dallo stesso cliente di Base44: Aurora contiene dati personali, Borea è un hub operativo e Cirro un gestionale territoriale. Aurora, Borea e Cirro sono nomi di fantasia: li uso per raccontare i casi senza rendere riconoscibili le applicazioni reali. Sono tre casi osservati nello stesso ambiente, non una misura di tutte le applicazioni della piattaforma.
Cosa ho trovato oltre la schermata
In Aurora e Borea ho letto dati personali e aziendali attraverso API che non richiedevano autenticazione.
Nelle app selezionate il server accettava scritture anonime e attributi sensibili come il ruolo scelto dal client.
Su Borea e Cirro ho raggiunto flussi di registrazione che la UI non mostrava e ho ottenuto JWT validi.
Su Borea una richiesta anonima al broker LLM è stata accettata, restituendo un consumo di 40 crediti.
Una GET e i dati erano già nella risposta: Broken Access Control
In Borea, l’hub operativo, mi è bastata una richiesta di lettura. Non ho inviato cookie o header Authorization. Qui chiamo l’entità BusinessRecord per non riportarne il nome originale.
Request
GET /api/apps/APP_ID_REDACTED/entities/BusinessRecord HTTP/1.1
Host: backend.example.test
Accept: application/json
Response
HTTP/1.1 200 OK
Content-Type: application/json
[
{
"id": "RECORD_ID_REDACTED",
"code": "CODE_REDACTED",
"amount": "AMOUNT_REDACTED",
"contact": "PERSON_REDACTED",
"note": "CONTENUTO_REDACTED"
}
]
Mostro un solo record. Nella verifica ne ho ottenuti 49. Attività, importi e referenti possono raccontare molto di un’azienda anche quando non contengono una password: cosa sta facendo, con chi lavora, quali decisioni sta prendendo.
In Aurora lo stesso tipo di esposizione riguardava 65 record con dati personali. In Cirro, invece, ho trovato sette entità interrogabili ma vuote. In quel momento non ne uscivano dati: era osservabile la possibilità di interrogarle senza autenticazione, non un’esposizione di record già presenti.
La famiglia è Broken Access Control. IDOR, chiamata BOLA nel contesto delle API, è una sua forma specifica. Si verifica quando posso cambiare l’identificativo di un oggetto e accedere al record di un altro utente. In questa richiesta non ho avuto bisogno di manipolare un ID. Il controllo mancava già sull’intera collezione.
Potevo anche scrivere, scegliendo il ruolo: Mass Assignment
La lettura era soltanto una parte del problema. Nelle applicazioni selezionate ho verificato anche la creazione anonima di record utente. Nel caso dell’entità User, ho inviato io stesso il valore admin nel campo del ruolo.
Request
POST /api/apps/APP_ID_REDACTED/entities/User HTTP/1.1
Host: backend.example.test
Content-Type: application/json
{
"email": "test@example.test",
"full_name": "UTENTE_DI_TEST",
"role": "admin"
}
Response
HTTP/1.1 200 OK
Content-Type: application/json
{
"id": "USER_ID_REDACTED",
"role": "admin",
"created_by_id": "anonymous"
}
Il server ha salvato il ruolo indicato nella richiesta e ha attribuito la creazione a anonymous. Questo dimostra una scrittura anonima con un attributo sensibile. Da solo non dimostra che quel record producesse una sessione amministrativa funzionante.
La distinzione conta, ma non riduce il problema di integrità: stavo introducendo dati dall’esterno in un sistema apparentemente riservato. Alcuni record di test, inoltre, non sono riuscito a eliminarli attraverso i percorsi provati.
Qui si sovrappongono due famiglie. La prima resta Broken Access Control, perché la creazione era disponibile senza autorizzazione. La seconda è Broken Object Property Level Authorization, la categoria API che comprende il Mass Assignment: il client poteva fornire una proprietà sensibile come role e il backend la salvava.
La UI non offriva la registrazione. Il backend sì: Authentication Failures
Su Borea e Cirro non vedevo un percorso di autoregistrazione nell’interfaccia esaminata. Attraverso il flusso nativo sono però riuscito a creare e verificare account di test, ottenendo JWT validi anche con caselle temporanee.
Qui avevo ottenuto account autenticabili, un risultato diverso dal semplice inserimento di record tramite entities/User. La distinzione fra i due percorsi, invisibile a chi usa la dashboard, diventava evidente osservando le richieste.
Su Borea ho inoltre osservato che il reinvio dell’OTP azzerava il contatore dei tentativi. Non è il numero di cifre, da solo, a raccontare la debolezza: è il comportamento del flusso quando lo percorro in un modo diverso da quello previsto dall’interfaccia.
Questa è la famiglia delle Authentication Failures, insieme a un bypass della business logic. Il percorso previsto dalla UI diceva che non ci si poteva registrare. Un percorso alternativo del backend permetteva invece di completare lo stesso obiettivo.
Una richiesta di impostazioni restituiva secret: Broken Access Control
Su Borea ho interrogato una funzione backend senza autenticarmi. Nella risposta ho trovato una chiave privata VAPID e un secret per webhook email.
Request
POST /api/apps/APP_ID_REDACTED/functions/api HTTP/1.1
Host: backend.example.test
Content-Type: application/json
{
"action": "get_impostazioni"
}
Response
HTTP/1.1 200 OK
Content-Type: application/json
{
"vapid_private_key": "SECRET_REDACTED",
"brevo_webhook_secret": "SECRET_REDACTED",
"brevo_sender_email": "sender@example.test"
}
Per chi usa l’applicazione, una pagina di impostazioni può sembrare un dettaglio amministrativo. Nella risposta che ho ricevuto c’era invece materiale riservato delle integrazioni.
L’eventuale abuso delle notifiche push o dei webhook dipende anche dagli altri elementi dell’integrazione. Il dato osservato è che quei secret uscivano dal backend senza autenticazione. Ho raccolto l’evidenza prima di un redeploy, dopo il quale la funzione restituiva 404. Questo non mi dice che cosa sia accaduto alle chiavi già esposte.
La radice è ancora Broken Access Control. L’esposizione di informazioni sensibili descrive l’impatto. Il problema che lo rende possibile è la funzione richiamabile senza un controllo di autorizzazione.
Il backend accettava anche chiamate AI a consumo: Resource Consumption
Il passaggio successivo aggiungeva una conseguenza economica. Su Borea ho effettuato una chiamata anonima al broker LLM, il servizio che inoltra le richieste ai modelli AI.
Request
POST /api/apps/APP_ID_REDACTED/ai/openai/v1/chat/completions HTTP/1.1
Host: backend.example.test
Content-Type: application/json
{
"model": "MODELLO_REDACTED",
"messages": [
{ "role": "user", "content": "ciao" }
]
}
Response, ridotta al consumo
HTTP/1.1 200 OK
Content-Type: application/json
{
"usage": {
"base44_credits": 40.0
}
}
La richiesta era anonima, ma il consumo era associato al cliente di Base44 che aveva creato l’app. In quella chiamata la risposta indicava 40 crediti, il valore osservato per quel modello.
Ho inviato anche tre richieste ravvicinate, tutte accettate con 200. Questo non dimostra consumi illimitati o l’assenza di qualsiasi soglia. Dimostra però che potevo attivare quel servizio a consumo senza una sessione applicativa.
È un cambio di prospettiva: oltre ai dati che possono uscire, ci sono risorse che qualcuno può consumare a carico di chi ha pubblicato l’applicazione.
OWASP classifica questo rischio API come Unrestricted Resource Consumption. C’è anche una componente di business logic: una funzione legittima e costosa può essere richiamata fuori dal flusso e dal soggetto per cui era stata pensata.
Il CORS, a quel punto, era l’ultimo dei problemi
Ho trovato anche un meccanismo custom di conservazione delle password basato su codifiche Base64 ripetute in Aurora, una stored XSS nella stessa applicazione e quindici tentativi di login senza risposta 429 su Cirro. Sono famiglie note: Cryptographic Failures, Injection e Authentication Failures. Ripetere Base64 lascia una trasformazione reversibile. I tentativi accettati descrivono la sequenza provata, senza dimostrare da soli l’assenza di ogni possibile blocco.
In questo contesto, il CORS permissivo, una Security Misconfiguration, passava decisamente in secondo piano. Avevo già ottenuto dati, scritto record e chiamato funzioni senza autenticazione. Le regole CORS governano ciò che una pagina può leggere da un’altra origine nel browser. Non impediscono a un client come curl di fare le stesse richieste. Guida CORS di MDN.
La riflessione: cosa stiamo delegando all’AI?
Quello che mi ha colpito è la normalità delle richieste: leggere dati, creare un utente, recuperare impostazioni.
Queste vulnerabilità esistevano molto prima dell’AI. Broken Access Control, IDOR o BOLA, Authentication Failures, Mass Assignment e abusi della business logic sono famiglie ben note. Con il vibe coding cambiano la velocità e può cambiare la scala: l’AI genera rapidamente Entity, funzioni e flussi di autenticazione.
Per mettere a fuoco il punto, guardo queste piattaforme come una SWOT. Non è un verdetto su Base44. È la fotografia della tensione che ho visto tra velocità e controllo.
Dall’idea a un prodotto completo
Interfaccia, backend, autenticazione e hosting arrivano nello stesso flusso. La distanza tra intenzione e prototipo si riduce drasticamente.
L’astrazione nasconde i confini
Broken Access Control e Authentication Failures possono restare dietro una dashboard che sembra protetta e funziona nel percorso previsto.
Più persone possono costruire
Anche un piccolo team può trasformare processi interni e idee in strumenti reali, iterando con grande velocità.
Lo stesso errore può moltiplicarsi
Un errore nei permessi può ripetersi su più Entity e funzioni generate dallo stesso modello, amplificando l’impatto.
La SWOT mette a fuoco i fattori; il grafico prova a tradurne la tensione in un numero. È un indice illustrativo, non uno standard: valuto cinque fattori su una scala da 1 a 5 e li combino come probabilità per impatto. In questo caso assegno il massimo a esposizione, ripetibilità, gap di competenza e impatto, e 4 all’opacità, per un totale di 23,8 su 25. Serve a rendere leggibile la tensione tra velocità e controllo, non a misurarla con precisione.
Indice illustrativo
La leva amplifica anche l’errore
(Esposizione + Ripetibilità + Opacità + Gap competenze) ÷ 4- Esposizione
- 5
- Ripetibilità
- 5
- Opacità
- 4
- Gap competenze
- 5
- Impatto
- 5
È un modello editoriale per leggere il caso, non una misura statistica.
Una dashboard funzionante dà una conferma immediata: il login riesce, l’elenco si carica, il riepilogo AI arriva. Ma questi passaggi non dicono se i permessi tengono quando le API vengono interrogate direttamente.
Questi tre casi appartengono allo stesso cliente della piattaforma Base44. Non dicono che ogni app o cliente sia esposto nello stesso modo. Mostrano però quanto possa essere profonda la distanza fra un’app che sembra riservata e il comportamento reale delle sue API.
L’AI è ormai uno strumento di lavoro e di sviluppo. La questione è chi la accompagna. Dietro un’app generata non può esserci soltanto qualcuno capace di descrivere cosa desidera. Serve una persona con competenze sufficienti per misurare il comportamento prodotto, leggere ciò che l’AI ha costruito e capire se le sue decisioni rispettano davvero il contesto.
Delegare la scrittura del codice non elimina il bisogno di giudizio tecnico. Lo rende più importante: qualcuno deve saper verificare che il comportamento generato corrisponda davvero a ciò che si voleva costruire.