

- Ivan Luminaria/
- Database Strategy/
- Oracle/
- Utenti, ruoli e privilegi in Oracle: perché GRANT ALL non è mai la risposta/
Utenti, ruoli e privilegi in Oracle: perché GRANT ALL non è mai la risposta
Mi è capitato più volte di entrare in un ambiente Oracle e trovare la stessa situazione: tutti gli utenti applicativi connessi come schema owner, con il ruolo DBA assegnato. Sviluppatori, applicazioni batch, tool di reportistica — tutti con gli stessi privilegi dell’utente che possiede le tabelle.
Quando chiedi perché, la risposta è sempre una variante di: “Così funziona tutto senza problemi di permessi.”
Certo. Funziona tutto. Fino al giorno in cui uno sviluppatore lancia un DROP TABLE sulla tabella sbagliata. O un batch di import fa un TRUNCATE su una tabella di produzione pensando di essere in ambiente di test. O qualcuno esegue un DELETE FROM clienti senza la clausola WHERE.
Quel giorno la criticità non sono più i permessi. È che non hai idea di chi abbia fatto cosa, e non hai nessuno strumento per impedire che succeda di nuovo.
Il contesto: un classico che si ripete #
Il cliente era una media azienda con un’applicazione gestionale su Oracle 19c. Circa venti utenti tra sviluppatori, applicativi e operatori. Lo schema applicativo — chiamiamolo APP_OWNER — conteneva circa 300 tabelle, una sessantina di viste e qualche decina di procedure PL/SQL.
La situazione era semplice da descrivere:
- Tutti si collegavano come
APP_OWNER APP_OWNERaveva il ruoloDBA- Nessun audit configurato
- Nessuna separazione tra chi legge e chi scrive
- Le password erano condivise via email
Non era negligenza. Era inerzia. Il sistema era cresciuto così nel corso degli anni, e nessuno si era mai fermato a ripensare il modello. Funzionava, e questo bastava.
Fino a quando un operatore ha cancellato per errore i dati di fatturazione di un intero trimestre. Nessun log, nessuna traccia, nessun colpevole identificabile. Solo un backup di due giorni prima e un buco nei dati che ha richiesto settimane di lavoro per essere colmato.
Come funziona la sicurezza in Oracle: il modello #
Prima di raccontare cosa ho fatto, serve capire come Oracle struttura la sicurezza. Il modello è diverso da PostgreSQL e da MySQL, e le differenze non sono cosmetiche.
Utente e schema: la stessa cosa (quasi) #
In Oracle, creare un utente significa creare uno schema. Non sono due concetti separati: l’utente APP_OWNER è anche lo schema APP_OWNER, e gli oggetti creati da quell’utente vivono in quello schema.
CREATE USER app_read IDENTIFIED BY "PasswordSicura#2026"
DEFAULT TABLESPACE users
TEMPORARY TABLESPACE temp
QUOTA 0 ON users;
La QUOTA 0 è intenzionale: questo utente non deve creare oggetti. È un consumatore, non un proprietario.
Privilegi di sistema vs privilegi di oggetto #
Oracle distingue nettamente tra:
- System privileges: operazioni globali come
CREATE TABLE,CREATE SESSION,ALTER SYSTEM - Object privileges: operazioni su oggetti specifici come
SELECT ON app_owner.clienti,EXECUTE ON app_owner.pkg_fatture
Il ruolo DBA include oltre 200 system privilege. Assegnarlo a un utente applicativo è come dare le chiavi dell’intero palazzo a chi deve solo entrare in una stanza.
I ruoli: predefiniti e custom #
Oracle offre ruoli predefiniti (CONNECT, RESOURCE, DBA) e permette di crearne di custom. I ruoli predefiniti hanno un punto critico storico: CONNECT e RESOURCE includevano privilegi eccessivi nelle versioni più vecchie. Da Oracle 12c in poi sono stati ridimensionati, ma l’abitudine di assegnarli senza pensarci è dura a morire.
La strada giusta è creare ruoli custom calibrati sulle reali necessità.
L’implementazione: tre ruoli, zero ambiguità #
Ho progettato tre ruoli: lettura, scrittura e amministrazione applicativa.
1. Ruolo di sola lettura #
CREATE ROLE app_read_role;
-- Privilegi sulle tabelle
GRANT SELECT ON app_owner.clienti TO app_read_role;
GRANT SELECT ON app_owner.ordini TO app_read_role;
GRANT SELECT ON app_owner.fatture TO app_read_role;
GRANT SELECT ON app_owner.prodotti TO app_read_role;
GRANT SELECT ON app_owner.movimenti TO app_read_role;
-- Privilegi sulle viste
GRANT SELECT ON app_owner.v_report_vendite TO app_read_role;
GRANT SELECT ON app_owner.v_stato_ordini TO app_read_role;
In un ambiente con 300 tabelle non le elenchi una per una a mano. Ho usato un blocco PL/SQL per generare i grant:
BEGIN
FOR t IN (SELECT table_name FROM dba_tables
WHERE owner = 'APP_OWNER') LOOP
EXECUTE IMMEDIATE 'GRANT SELECT ON app_owner.'
|| t.table_name || ' TO app_read_role';
END LOOP;
END;
/
Semplice, ripetibile, e soprattutto: documentato. Perché tra sei mesi qualcuno dovrà capire cosa è stato fatto e perché.
2. Ruolo di lettura e scrittura #
CREATE ROLE app_write_role;
-- Eredita tutto dal ruolo di lettura
GRANT app_read_role TO app_write_role;
-- Aggiunge DML sulle tabelle operative
GRANT INSERT, UPDATE, DELETE ON app_owner.ordini TO app_write_role;
GRANT INSERT, UPDATE, DELETE ON app_owner.movimenti TO app_write_role;
GRANT INSERT, UPDATE ON app_owner.clienti TO app_write_role;
-- Permesso di esecuzione sulle procedure applicative
GRANT EXECUTE ON app_owner.pkg_ordini TO app_write_role;
GRANT EXECUTE ON app_owner.pkg_fatture TO app_write_role;
Nota: niente DELETE sulla tabella clienti. Non perché sia tecnicamente impossibile, ma perché il processo applicativo prevede una disattivazione, non una cancellazione. Il privilegio riflette il processo, non la comodità.
3. Ruolo di amministrazione applicativa #
CREATE ROLE app_admin_role;
-- Eredita il ruolo di scrittura
GRANT app_write_role TO app_admin_role;
-- Aggiunge DDL controllato
GRANT CREATE VIEW TO app_admin_role;
GRANT CREATE PROCEDURE TO app_admin_role;
GRANT CREATE SYNONYM TO app_admin_role;
-- Può gestire le tabelle di configurazione
GRANT INSERT, UPDATE, DELETE ON app_owner.parametri TO app_admin_role;
GRANT INSERT, UPDATE, DELETE ON app_owner.lookup_tipi TO app_admin_role;
Niente CREATE TABLE, niente DROP ANY, niente ALTER SYSTEM. L’admin applicativo gestisce la logica, non la struttura fisica.
Creazione degli utenti e assegnazione #
-- Utente per i report (sola lettura)
CREATE USER srv_report IDENTIFIED BY "RptSecure#2026"
DEFAULT TABLESPACE users TEMPORARY TABLESPACE temp
QUOTA 0 ON users;
GRANT CREATE SESSION TO srv_report;
GRANT app_read_role TO srv_report;
-- Utente applicativo (lettura e scrittura)
CREATE USER srv_app IDENTIFIED BY "AppSecure#2026"
DEFAULT TABLESPACE users TEMPORARY TABLESPACE temp
QUOTA 0 ON users;
GRANT CREATE SESSION TO srv_app;
GRANT app_write_role TO srv_app;
-- DBA applicativo (amministrazione)
CREATE USER dba_app IDENTIFIED BY "DbaSecure#2026"
DEFAULT TABLESPACE users TEMPORARY TABLESPACE temp
QUOTA 10M ON users;
GRANT CREATE SESSION TO dba_app;
GRANT app_admin_role TO dba_app;
Ogni utente ha una password propria, un ruolo specifico e una quota disco coerente con il suo scopo. srv_report non ha quota perché non deve creare nulla. dba_app ha 10 MB perché deve poter creare viste e procedure.
Revoca del ruolo DBA #
Il passo più delicato: togliere il DBA a APP_OWNER.
REVOKE DBA FROM app_owner;
Una riga. Ma prima di eseguirla, ho verificato che APP_OWNER avesse ancora i privilegi necessari per possedere i propri oggetti:
SELECT privilege FROM dba_sys_privs WHERE grantee = 'APP_OWNER';
SELECT granted_role FROM dba_role_privs WHERE grantee = 'APP_OWNER';
E ho assegnato solo i privilegi strettamente necessari:
GRANT CREATE SESSION TO app_owner;
GRANT CREATE TABLE TO app_owner;
GRANT CREATE VIEW TO app_owner;
GRANT CREATE PROCEDURE TO app_owner;
GRANT CREATE SEQUENCE TO app_owner;
GRANT UNLIMITED TABLESPACE TO app_owner;
APP_OWNER rimane il proprietario degli oggetti, ma non ha più il potere di fare qualsiasi cosa sul database. È un proprietario, non un dio.
⚠️ Avvertenza: privilegi via role e stored procedure definer-rights #
C’è una trappola che vale la pena conoscere prima di scrivere codice PL/SQL contro questo modello a ruoli. Una regola Oracle che fa inciampare quasi tutti i DBA junior:
I privilegi concessi tramite role NON sono visibili dentro stored procedure compilate con definer-rights (default).
Esempio concreto. Concedo SELECT sulla tabella app_owner.fatture al ruolo app_read_role, e poi concedo il ruolo all’utente app_user:
GRANT SELECT ON app_owner.fatture TO app_read_role;
GRANT app_read_role TO app_user;
In sessione interattiva tutto funziona — app_user può fare SELECT * FROM app_owner.fatture. Ma se app_user ha una stored procedure che fa la stessa SELECT, al runtime Oracle restituisce ORA-00942: table or view does not exist. Lo stesso utente, la stessa query — un caso interattivo funziona, l’altro no.
Il motivo: di default le stored procedure Oracle vengono compilate con definer-rights (AUTHID DEFINER, comportamento implicito). Al runtime, il motore valuta i privilegi solo come diretti, non quelli ereditati via role. È un design choice di Oracle che esiste da decenni e che non cambia per retrocompatibilità.
Le due soluzioni:
Opzione 1 — Privilegio diretto all’owner della procedura:
-- Niente piu' role, GRANT diretto al singolo utente
GRANT SELECT ON app_owner.fatture TO app_user;
Funziona, ma annulla il vantaggio del modello a ruoli per quel privilegio specifico.
Opzione 2 — Stored procedure con invoker-rights (AUTHID CURRENT_USER):
CREATE OR REPLACE PROCEDURE leggi_fatture
AUTHID CURRENT_USER
AS
BEGIN
FOR r IN (SELECT * FROM app_owner.fatture) LOOP
-- ...
END LOOP;
END;
/
Con AUTHID CURRENT_USER, al runtime la procedura usa i privilegi del chiamante (incluse le role attive nella sessione). Mantiene il modello a ruoli intatto. Lo svantaggio: cambia la semantica di accesso agli oggetti (la procedura risolve i nomi nel contesto del chiamante, non del proprietario), quindi va valutato caso per caso.
La regola pratica: per procedure di lettura/scrittura applicative che lavorano sui dati di un proprietario centralizzato, AUTHID CURRENT_USER è quasi sempre la scelta giusta. Per procedure amministrative che devono operare con i privilegi specifici dell’owner, lascia il default AUTHID DEFINER e concedi i privilegi diretti.
Audit: sapere chi fa cosa #
Avere i ruoli giusti non basta. Serve sapere chi fa cosa, soprattutto sulle operazioni critiche.
Oracle dalla versione 12c offre Unified Audit, che sostituisce il vecchio audit tradizionale con un sistema centralizzato.
La sintassi prevede due statement distinti: CREATE AUDIT POLICY per dichiarare la policy, e AUDIT POLICY per attivarla [1]. Un errore comune è usare ALTER AUDIT POLICY ... ENABLE: ALTER AUDIT POLICY esiste, ma serve per modificare una policy già definita (aggiungere o togliere azioni), non per abilitarla.
-- Audit su operazioni DDL critiche
CREATE AUDIT POLICY pol_ddl_critico
ACTIONS CREATE TABLE, DROP TABLE, ALTER TABLE,
TRUNCATE TABLE, CREATE USER, DROP USER,
ALTER USER, GRANT, REVOKE;
AUDIT POLICY pol_ddl_critico;
-- Audit su accessi sensibili
CREATE AUDIT POLICY pol_accesso_dati
ACTIONS SELECT ON app_owner.clienti,
DELETE ON app_owner.fatture,
UPDATE ON app_owner.fatture;
AUDIT POLICY pol_accesso_dati;
-- Audit sui login falliti
CREATE AUDIT POLICY pol_login_falliti
ACTIONS LOGON;
AUDIT POLICY pol_login_falliti WHENEVER NOT SUCCESSFUL;
Per disabilitare una policy si usa NOAUDIT POLICY nome_policy.
Per verificare cosa viene registrato, si interroga la view UNIFIED_AUDIT_TRAIL [2]:
SELECT * FROM unified_audit_trail
WHERE event_timestamp > SYSDATE - 7
ORDER BY event_timestamp DESC;
L’audit non è paranoia. È l’unico modo per rispondere alla domanda “chi ha fatto cosa?” senza dover andare a intuizione.
Il confronto con PostgreSQL e MySQL #
Questo articolo è il terzo di una serie sulla gestione della sicurezza nei database relazionali. I primi due coprono PostgreSQL e MySQL.
Le differenze tra i tre sistemi sono sostanziali:
| Aspetto | Oracle | PostgreSQL | MySQL |
|---|---|---|---|
| Utente = schema? | Sì | No (indipendenti) | Sì (database separati) |
| Modello ruoli | Ruoli predefiniti + custom | Tutto è un ROLE | Ruoli da MySQL 8.0 |
| Identità | Nome utente | Nome utente | Coppia utente@host |
| Audit nativo | Unified Audit (12c+) | pgAudit (estensione) | Audit plugin |
| Privilegi granulari | System + Object | Database/Schema/Object | Global/DB/Table/Column |
| GRANT ALL | Esiste ma pericoloso | Esiste, sconsigliato | Esiste, sconsigliato |
In PostgreSQL tutto è un ROLE, e la semplicità del modello è il suo punto di forza. In MySQL l’identità è legata all’host di origine, il che aggiunge un livello di complessità (e di sicurezza) che gli altri non hanno. In Oracle il modello è il più ricco e il più granulare, ma anche il più facile da configurare male per eccesso di opzioni.
Il principio resta lo stesso ovunque: dai a ciascuno solo quello che gli serve, non un privilegio in più.
Cosa è cambiato dopo #
Il passaggio è stato graduale — due settimane per il rollout completo, con test su ogni applicativo e procedura. Qualche script ha smesso di funzionare perché dava per scontato di avere privilegi che non gli spettavano. Ogni mancato funzionamento era in realtà una criticità nascosta che prima era invisibile.
Il risultato:
- 20 utenti nominali al posto di un unico schema condiviso
- 3 ruoli custom al posto del ruolo DBA
- Audit attivo su DDL e operazioni sensibili
- Zero incidenti di cancellazione accidentale nei mesi successivi
Il cliente non ha notato miglioramenti nelle performance. Non era quello l’obiettivo. Ha notato che quando qualcuno sbagliava, il danno era contenuto e tracciabile. E questo, in un ambiente di produzione, vale più di qualsiasi ottimizzazione.
Conclusione #
GRANT ALL PRIVILEGES e il ruolo DBA sono scorciatoie. Funzionano nel senso che eliminano gli errori di permesso. Ma eliminano anche qualsiasi protezione.
La sicurezza in Oracle non è questione di strumenti — gli strumenti ci sono, e sono potenti. È questione di progettazione: decidere chi può fare cosa, documentarlo, implementarlo e poi verificare che funzioni.
Non è il lavoro più glamour del mondo. Ma è quello che fa la differenza tra un database che sopravvive e uno che è davvero sotto controllo.
Fonti ufficiali #
- Oracle Database SQL Language Reference 19c — AUDIT (Unified Auditing) e CREATE AUDIT POLICY
- Oracle Database Reference 19c — UNIFIED_AUDIT_TRAIL
Glossario #
System Privilege — Privilegio Oracle che autorizza operazioni globali sul database come CREATE TABLE, CREATE SESSION o ALTER SYSTEM, indipendenti da qualsiasi oggetto specifico.
Object Privilege — Privilegio Oracle che autorizza operazioni su un oggetto specifico del database come SELECT, INSERT o EXECUTE su una tabella, vista o procedura.
REVOKE — Comando SQL per rimuovere privilegi o ruoli precedentemente assegnati a un utente o ruolo, complementare al comando GRANT.
Unified Audit — Sistema di audit centralizzato introdotto in Oracle 12c che unifica tutti i tipi di audit in un’unica infrastruttura, sostituendo il vecchio audit tradizionale.
Least Privilege — Principio di sicurezza che prevede l’assegnazione a ogni utente solo dei permessi strettamente necessari per svolgere la propria funzione.
