Skip to content
CyberXplore - Xplore the Unseen

Broken Object-Level Authorization (BOLA / IDOR): il bug delle API che troviamo più spesso

cyberxploreDi cyberxplore13 min di lettura

Una vulnerabilità BOLA (chiamata anche IDOR) è il bug che segnaliamo più spesso nei test sulle API. Ecco come lo scoviamo scambiando gli ID degli oggetti tra due account, e come chiuderlo con un’autorizzazione lato server.

Broken Object-Level Authorization (BOLA / IDOR): il bug delle API che troviamo più spesso

Cambiate un solo numero in una URL e vi ritrovate a leggere la fattura di qualcun altro. È tutto qui il trucco dietro una vulnerabilità BOLA, ed è il rilievo che documentiamo più di ogni altro nelle attività sulle API. L’endpoint si comporta alla perfezione per l’utente autenticato. Restituisce un JSON pulito, supera ogni test funzionale e poi consegna in silenzio qualsiasi record chiediate, purché ne conosciate l’ID.

Broken Object-Level Authorization (BOLA) è il nome, nell’era delle API, di ciò che la maggior parte degli sviluppatori chiama ancora IDOR, una Insecure Direct Object Reference. Occupa il primo posto dell’OWASP API Security Top 10 con la sigla API1:2023, ed è un posto meritato. È facile da mandare in produzione, sopravvive alla revisione del codice ed è invisibile a uno scanner che non ha la minima idea di come sia fatto il vostro modello dati. Su tutti i backend REST e GraphQL che testiamo, questa singola categoria produce una fetta enorme dei nostri rilievi alti e critici.

Il lato positivo è che una vulnerabilità BOLA è anche uno dei bug più facili da prevenire dell’elenco, una volta che ne conoscete la forma. Questo articolo spiega che cos’è, come la scoviamo durante un’attività reale, che aspetto hanno la richiesta e la risposta grezze, i danni che provoca in produzione e la correzione che regge davvero.

Punti chiave

  • Una vulnerabilità BOLA (chiamata anche IDOR) si verifica quando un’API restituisce o modifica un oggetto a partire da un ID fornito dal client senza verificare che il chiamante sia autorizzato ad accedere a quell’oggetto.
  • È l’OWASP API1:2023 (Broken Object Level Authorization) e corrisponde alla CWE-639, Authorization Bypass Through User-Controlled Key.
  • La troviamo autenticandoci con due account distinti e rigiocando le richieste dell’account A con il token dell’account B, scambiando gli ID degli oggetti e tenendo d’occhio le risposte 200 che divulgano i dati dell’utente sbagliato.
  • Gli ID interi sequenziali rendono banale l’enumerazione di massa, ma gli UUID e gli hash non correggono il bug. Si limitano a rallentare i tentativi.
  • L’unica correzione affidabile è un controllo di autorizzazione lato server su ogni oggetto, a ogni richiesta, legato alla sessione autenticata e non a un qualsiasi ID inviato dal client.

Che cos’è una vulnerabilità BOLA (e in cosa si differenzia dall’IDOR)?

Una vulnerabilità BOLA è il mancato accertamento che il chiamante autenticato sia autorizzato ad agire sull’oggetto specifico che ha richiesto. L’API vi autentica senza problemi. Sa perfettamente chi siete. Poi prende un ID di oggetto direttamente dalla richiesta e salta l’unica domanda che conta: questo oggetto appartiene a voi?

IDOR e BOLA descrivono la stessa causa profonda. IDOR è il termine web più vecchio e più ampio. BOLA è il nome che OWASP ha scelto per la variante a livello di oggetto quando ha costruito l’API Security Top 10, perché le API espongono identificatori di oggetti ovunque e quello schema è diventato il rischio dominante delle API. Quando scriviamo “IDOR” in un report, intendiamo esattamente questo: il riferimento all’oggetto è diretto, controllato dall’utente e non protetto. In tutto questo articolo usiamo entrambi i termini per lo stesso rilievo.

È utile distinguere BOLA dalla sua cugina, la Broken Function Level Authorization (API5:2023). I bug a livello di funzione riguardano il richiamo di un’azione che non dovreste poter richiamare affatto, come raggiungere una rotta riservata agli amministratori. I bug a livello di oggetto riguardano il richiamo di un endpoint perfettamente legittimo su dati che non sono vostri. Potete avere ogni rotta blindata e comunque lasciar sfuggire ogni scheda cliente attraverso un singolo parametro id non controllato.

Perché le API REST e GraphQL ne sono così soggette?

Le API sono soggette al BOLA perché sono costruite attorno a oggetti indirizzabili, e ognuno di quegli oggetti ha bisogno della propria guardia. Un’applicazione classica renderizzata sul server tende a limitare i dati alla sessione per caso. La pagina interroga sempre e solo i “miei” ordini, quindi non c’è nulla da recuperare tramite un ID arbitrario. Un’API fa l’opposto. Pubblica una superficie uniforme come /api/orders/{id} e invita il client a nominare direttamente l’oggetto. Moltiplicate questo per qualche centinaio di endpoint e qualche decina di tipi di oggetti, e la probabilità che uno di essi abbia dimenticato il proprio controllo di appartenenza sale in fretta.

REST mette l’identificatore in bella vista. Sta nel percorso o in una query string, spesso è un intero sequenziale e bastano una decina di secondi per fuzzarlo. GraphQL nasconde lo stesso problema altrove. Un unico endpoint accetta un argomento id su resolver annidati, e l’autorizzazione va applicata a livello di campo e di nodo. Gli sviluppatori che proteggono la query di primo livello mancano regolarmente le ricerche annidate node(id: ...), gli alias raggruppati o gli edge di connessione che risolvono gli oggetti per conto proprio. L’esito è identico: passate un ID che non vi appartiene e ottenete dati che non dovreste vedere.

I microservizi aggiungono un ulteriore modo di fallire. Non appena una richiesta attraversa un confine di fiducia interno, i servizi a valle tendono a presumere che il gateway abbia già gestito l’autorizzazione, e così risolvono allegramente qualsiasi ID di oggetto capiti loro davanti.

Come scoviamo una vulnerabilità BOLA durante un’attività?

Il nostro metodo è volutamente noioso: autenticarci con due account distinti a bassi privilegi, poi provare a far accedere un account ai dati dell’altro. È proprio la noia a trovare bug reali in modo costante. Ecco il ciclo.

Per prima cosa mappiamo la superficie degli oggetti. Facciamo passare l’applicazione attraverso Burp Suite, percorriamo ogni funzionalità come utente A e cataloghiamo ogni endpoint che accetta un identificatore di oggetto. ID di ordini, ID di documenti, thread di messaggi, numeri di fattura, handle di profilo, chiavi di file. Tutto ciò che il client può nominare finisce nell’elenco.

Poi caratterizziamo gli identificatori. Interi sequenziali significano che un attaccante può percorrere l’intero set di dati contando a partire da 1. Gli UUID, gli ULID e gli hash alzano lo sforzo, e li trattiamo come un dosso, non come un controllo. Continuiamo a trovare ID “impossibili da indovinare” che trapelano attraverso qualche altro canale: una vista a elenco, una ricevuta via e-mail, un link di referral, un edge GraphQL, una stack trace in una risposta di errore. Una volta che l’ID è esposto da qualche parte, la sua entropia smette di contare.

Poi arriva lo scambio a due account. Catturiamo le richieste dell’utente A e le rigiochiamo con il token di sessione dell’utente B, cambiando solo l’ID dell’oggetto in modo che punti di nuovo alla risorsa di A. È qui che l’estensione Autorize per Burp si guadagna il suo posto nella barra degli strumenti. Datele il cookie o il bearer token dell’utente B e ripeterà in silenzio ogni richiesta che effettuate come B, poi etichetterà le risposte in verde (applicata), in rosso (aggirata) o in arancione quando non riesce a decidere e vuole che sia un umano a guardarci. Una riga rossa con i dati di A nella risposta di B è una vulnerabilità BOLA, punto e basta.

Non ci fermiamo mai al GET. Alcuni dei rilievi più brutti che consegniamo sono su PUT, PATCH e DELETE, dove un controllo mancante consente a un utente di sovrascrivere o distruggere i record di un altro. Spostiamo anche l’identificatore: un ID che viene controllato nella URL a volte viene ignorato quando lo stesso valore viene inserito in un corpo JSON, e un campo di mass-assignment come "userId" può riassegnare in silenzio l’oggetto a qualcun altro. Per l’enumerazione su larga scala pilotiamo il parametro dell’ID con ffuf o Burp Intruder e confrontiamo codici di stato e lunghezze delle risposte per individuare quali ID restituiscono dati reali rispetto a un 404 uniforme.

Che aspetto ha davvero la richiesta vulnerabile?

Ecco un esempio anonimizzato contro un bersaglio fittizio. L’utente B è autenticato con il proprio token valido, ma richiede l’ordine 1024, che appartiene all’utente A.

GET /api/orders/1024 HTTP/1.1
Host: api.example.com
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.userB-token
Accept: application/json

Un’API sicura risponde 404 Not Found o 403 Forbidden, perché l’ordine 1024 non è associato all’utente B. Una vulnerabile autentica il token, cerca l’ordine solo tramite l’ID e lo serializza direttamente in risposta:

HTTP/1.1 200 OK
Content-Type: application/json

{
  "orderId": 1024,
  "customer": "Alice Nguyen",
  "email": "[email protected]",
  "shippingAddress": "742 Evergreen Terrace",
  "items": [ { "sku": "SKU-88213", "qty": 2 } ],
  "total": "184.00",
  "paymentLast4": "4242"
}

Il token è dell’utente B. I dati sono dell’utente A. Nulla nella risposta è malformato. Il server ha fatto esattamente ciò che il codice gli ha detto di fare, cioè recuperare l’ordine 1024 e restituirlo. Sostituite 1024 con 1025, poi con 1026, e vi siete fatti strada via script attraverso l’intera tabella degli ordini. Questo è il bug nella sua forma più pura.

Qual è l’impatto reale?

L’impatto è un’esposizione massiva di dati, spesso con una compromissione degli account che ci si accavalla sopra. Poiché l’enumerazione è automatizzabile via script, un singolo endpoint non protetto non divulga un solo record. Li divulga tutti. Abbiamo visto falle a livello di oggetto esporre i dati personali completi dei clienti, gli storici degli ordini, i metadati di pagamento, i messaggi privati e i documenti interni, il tutto raggiungibile tramite un ciclo che conta gli ID mentre voi andate a prendervi un caffè.

Raramente si ferma alla lettura. Se lo stesso schema vive su un endpoint di profilo o di impostazioni che accetta PATCH, un attaccante può cambiare l’indirizzo e-mail o il numero di telefono di un altro utente, poi far partire un reset della password verso l’indirizzo che ora controlla, e il bug di lettura diventa una compromissione completa dell’account. Se l’oggetto è una chiave API, un segreto di webhook o un token di invito, la fuga si trasforma in un punto d’appoggio per un accesso più profondo. Le autorità di controllo trattano l’accesso non autorizzato a dati personali come una violazione a prescindere da quanto banale fosse la tecnica, quindi il conto per la notifica e la conformità arriva esattamente come arriverebbe per qualsiasi altra esposizione.

Come si previene una vulnerabilità BOLA?

Applicate un controllo di autorizzazione lato server su ogni oggetto, a ogni richiesta, legato alla sessione autenticata e non a qualcosa che il client invia. Quell’unica frase è la correzione. Il resto è solo come renderla affidabile.

  • Negare per impostazione predefinita. L’accesso a un oggetto dovrebbe fallire in modo chiuso a meno che una regola esplicita non stabilisca che il principal corrente può vederlo. Un nuovo endpoint dovrebbe ereditare il blocco, non l’accesso.
  • Non trattate mai un ID fornito dal client come una decisione di autorizzazione. L’ID vi dice quale oggetto. La sessione vi dice chi sta chiedendo. L’autorizzazione è il collegamento tra i due, e quel collegamento spetta al server ogni singola volta.
  • Limitate la query al proprietario. Lo schema più duraturo che consigliamo è l’accesso ai dati limitato all’oggetto: interrogate WHERE order.id = :id AND order.owner_id = :currentUser invece di recuperare per ID e verificare l’appartenenza dopo. Una riga che non è vostra semplicemente non si carica mai, quindi non c’è alcuno scarto tra caricamento e controllo che qualcuno possa dimenticare.
  • Centralizzate il controllo. Mettete l’autorizzazione in uno strato di policy o in un middleware attraverso cui passano ogni resolver e ogni controller, invece di copiare e incollare un controllo di appartenenza in ogni handler. I controlli sparsi sono esattamente il motivo per cui un endpoint viene dimenticato.
  • In GraphQL, autorizzate a livello di nodo e di campo. Proteggete ogni resolver che carica un oggetto tramite ID, comprese le ricerche node annidate, gli edge di connessione e gli alias raggruppati, non solo la query di primo livello.
  • Usate ID difficili da indovinare come difesa in profondità, non come il controllo. Gli UUID riducono l’enumerazione alla cieca, ma non sostituiscono mai il controllo di appartenenza.

Già che ci siete, integrate una copertura di regressione. Un test che effettua il login come utente B e verifica un 403 o un 404 sull’oggetto dell’utente A costa pochi minuti da scrivere e intercetta il giorno in cui qualcuno rifattorizza la query e lascia cadere in silenzio la restrizione.

Come aiuta CyberXplore

L’autorizzazione a livello di oggetto è esattamente il tipo di falla che gli scanner automatici mancano e che i test manuali intercettano, perché dipende dalla comprensione del vostro modello dati e dall’esecuzione del confronto a due account contro la logica di business reale. Il nostro team di penetration test delle API mappa ogni endpoint che porta oggetti, rigioca le richieste tra account distinti e convalida ogni rilievo con una richiesta riproducibile come quella qui sopra, poi vi consegna indicazioni di correzione per chiuderlo. Se desiderate una valutazione focalizzata sul BOLA della vostra API REST o GraphQL, richiedete un preventivo e la struttureremo attorno ai vostri endpoint reali.

FAQ

BOLA è la stessa cosa di IDOR?

In pratica sì. IDOR (Insecure Direct Object Reference) è il termine web più vecchio e generale, e BOLA (Broken Object Level Authorization) è il nome che OWASP usa per la versione a livello di oggetto nell’API Security Top 10. Entrambi condividono la stessa causa profonda: il server agisce su un ID di oggetto fornito dal client senza confermare che il chiamante sia autorizzato per quell’oggetto. Usiamo entrambi i termini per lo stesso rilievo.

Dove si colloca BOLA nell’OWASP API Security Top 10?

È l’API1:2023, Broken Object Level Authorization, la prima voce dell’elenco del 2023. Corrisponde anche alla CWE-639, Authorization Bypass Through User-Controlled Key. Citarle entrambe in un report offre agli sviluppatori un riferimento preciso e ancorato agli standard per questa categoria di bug e per la sua remediation.

Gli UUID prevengono i bug IDOR nelle API?

No. Gli UUID casuali rendono l’enumerazione alla cieca molto più difficile, quindi aiutano contro un attaccante di passaggio che tira a indovinare gli ID, ma non aggiungono alcuna autorizzazione. Se l’ID trapela attraverso un endpoint a elenco, una e-mail, un link di referral o un edge GraphQL, cosa che accade di frequente, l’oggetto resta spalancato. Trattate gli ID impossibili da indovinare come difesa in profondità, mai come il controllo di accesso in sé.

Uno scanner di vulnerabilità può trovare BOLA?

Raramente da solo. Gli scanner non sanno quale oggetto appartenga a quale utente, quindi non possono accorgersi che una risposta 200 dall’aspetto valido sta in realtà divulgando i dati di un altro account. Trovare BOLA in modo affidabile richiede test autenticati con almeno due account e uno strumento come l’Autorize di Burp per confrontare le risposte. Ecco perché resta vicino alla cima dei rilievi nei test manuali sulle API.

BOLA riguarda solo le richieste GET?

No, e le operazioni di scrittura sono spesso peggiori. Un controllo sull’oggetto mancante su PUT, PATCH o DELETE consente a un attaccante di modificare o eliminare record che appartengono ad altri utenti, e un campo scrivibile come un indirizzo e-mail può concatenarsi fino a una compromissione dell’account. Testiamo ogni metodo contro ogni endpoint che porta oggetti, non solo le letture.

Con quanta rapidità possiamo correggere un rilievo BOLA confermato?

Spesso nell’arco di uno sprint. La correzione più robusta consiste nel limitare ogni query sui dati al proprietario autenticato, così le righe non autorizzate non si caricano mai, e nel centralizzare quel controllo in uno strato di policy invece che in codice per singolo handler. Aggiungete un test di regressione che verifichi un 403 o un 404 quando un account richiede l’oggetto di un altro, e la categoria resta chiusa man mano che il codice continua a cambiare.

Lavora con CyberXplore

Penetration Testing delle API

Vedi questo rischio nei tuoi sistemi? I nostri tester senior trovano e dimostrano esattamente questi problemi e ti danno un percorso chiaro per risolverli.

Articoli correlati

Trasforma questi approfondimenti in un progetto

Ottieni un penetration test guidato da esperti senior e su misura per il tuo stack - risultati concreti, non una checklist.

  • Retest gratuito di ogni correzione
  • Scope e preventivo in 24 ore
  • Solo tester senior
  • ISO 27001
  • ISO 9001
  • OSCP
  • CRTP
  • CREST
Richiedi un preventivo