Auditar y limitar la modificación de esquemas en Oracle
Índice
Motivación
Por todos es conocido la importancia que tiene una buena gestión de los datos en una entidad. Hacer una buena gestión precisa de recursos, no solo tecnológicos, sino también humanos.
Es necesario disponer de personal cualificado que no solo permita hacer una correcta administración de los sistemas gestores de bases de datos, sino que también tenga el conocimiento matemático (cálculo relacional1) para realizar la abstracción de la información que se va a almacenar estableciendo los criterios de integridad, unicidad y dominio que correspondan conforme al sistema gestor y a la información, consiguiendo eficiencia en el almacenamiento y el rendimiento que se desea.
Así, es común que en empresas y administraciones públicas los mismos desarrolladores de aplicaciones diseñen, instancien y actualicen los esquemas de bases de datos sin tener en cuenta los criterios mencionados anteriormente. Con cierta frecuencia acaban ignorados otros esquemas de bases de datos de la organización que manejan datos relevantes para la aplicación en cuestión.
Acaba siendo habitual descubrir que un mismo conjunto de datos esté total o parcialmente duplicado en distintos esquemas, con definiciones diferentes sin referencias de integridad pero que son semánticamente correspondientes, generando versiones diferentes de una misma información: islas de conocimiento inconexas.
En la práctica —por ejemplo, en casos de carga excesiva de trabajo— muchos DBA acaban delegando a los programadores la modificación de los esquemas. Se hace preciso un mecanismo que registre los cambios y modificaciones para auditorías y análisis retrospectivos.
Caso de estudio: situación de partida
Atendemos al caso de una administración pública de tamaño medio, que ofrece servicio a unos cinco mil usuarios internos, con un gestor de bases de datos Oracle con más de 500 esquemas sirviendo más de cien aplicaciones de desarrollo propio o externo.
El equipo DBA atiende a las funciones principales:
- Administración del sistema gestor de base de datos a nivel físico (servicios, almacenamiento, replicación, configuraciones de alta disponibilidad, etc).
- Administración de usuarios de bases de datos.
- Creación y mantenimiento de esquemas.
- Revisar y aprobar las propuestas de modificaciones de esquemas
- Proponer alternativas y mejoras.
La institución cuenta con un equipo propio de programación de aplicaciones basadas en Java con ORM Hibernate. Hasta la implantación de la figura del DBA, históricamente el propio equipo administraba bases de datos y esquemas. El modelado de la información no era riguroso y desde entonces se heredan islas de información, tablas duplicadas, etc.
En la actualidad, pensando en la agilidad del trabajo, la dirección implanta la posibilidad de que sean los desarrolladores los que modifiquen los esquemas propios de sus aplicaciones, garantizando la estabilidad de los esquemas generales. Al equipo DBA le queda la responsabilidad de vigilar qué se hace y resolver las dudas y problemas que aparezcan. A continuación explicamos cómo implementar esta vigilancia.
Estudio
¿Cómo poner en marcha la vigilancia de cambios en el esquema? La solución más obvia es capturar con disparadores2 las sentencias SQL DDL3 de modificación de esquemas.
La captura de todas las sentencias SQL DDL en Oracle4 tendría la forma:
create or replace trigger NOMBRE_DEL_DISPARADOR
before ddl on ESQUEMA_A_AUDITAR.schema
...
Con before ddl
indicamos a Oracle que debe ejecutar el disparador antes de ejecutar cualquier sentencia DDL. Así, conseguimos:
- Registrar la sentencia en
ESQUEMA_A_AUDITAR.schema
. - Aprovar o rechazar la ejecución de la sentencia, según se convenga.
Al activarse el disparador Oracle automáticamente incluye en la llamada unos atributos5 con información de qué se está ejecutando. Entre ellos nos interesan:
ora_dict_obj_type
: tipo de objeto de base de datos sobre el que se está ejecutando la sentencia DDL.ora_dict_obj_owner
: esquema del objeto que está siendo modificado.ora_dict_obj_name
: nombre del objeto de esquema que está siendo modificado.ora_sysevent
: tipo de sentencia DDL en ejecución.ora_login_user
: nombre del usuario de base de datos autenticado que ejecuta la modificación.ora_sql_txt
: función que devuelve la sentencia DDL ejecutada.sys_context
6: función que informa del contexto de la conexión del usuario.
Así, cada vez que se active el disparador se registrarán las sentencias DDL y, cuando proceda, se lanzarán errores de aplicación para los casos que se establezcan prohibidos.
En nuestro caso no se permitirán cambios de esquema cuando:
- un usuario sin rol
DBA
intente modificar o eliminar disparadores de auditoría, privilegio restringido al equipo DBA; - se usen ciertas sentencias DDL explícitamente prohibidas (
ALTER TABLE
,TRUNCATE
, etc) - o cuando se intenten modificar esquemas explícitamente prohibidos.
Cada esquema a auditar necesita de su propio disparador.
Debe establecerse una estrategia de seguimiento de esquemas. Por ejemplo, no es necesario auditar algunos esquemas cuyas modificaciones sean habituales (por creación de vistas o tablas temporales en la propia ejecución de la aplicación, por ejemplo) o son esquemas de aplicaciones externas mantenidas por el proveedor.
Es necesario especificar:
- Qué esquemas van a ser auditados y cuáles no (teniendo prevalencia la exclusión sobre la inclusión).
- En los esquemas en seguimiento, qué objetos no van a ser auditados.
Ejecución
Antes de crear los disparadores debemos crear el conjunto de objetos que almacenarán los datos de auditoría.
Creación del esquema auditoria
:
-- Creación del espacio de tablas. Los ficheros de datos se crean en ASM (en este caso).
create tablespace auditoria datafile '+DATA_PRODUCCION'
size 100m logging extent management local segment space management auto
;
-- Creación del usario y esquema.
create user auditoria
identified by "***" account unlock
default tablespace auditoria quota unlimited on auditoria temporary tablespace temp
;
-- Otorgamiento del permiso para iniciar sesión.
grant connect to auditoria;
Los nombres de los ficheros de datos del espacio de tablas auditoria
usan la nomenclatura ASM.
Si no usa ASM, es necesario incluir en create tablespace
7 la ruta al fichero en el sistema de archivos.
Las tablas de auditoría se crean con:
-- Establecimiento del esquema por defecto para esta sesión.
alter session set current_schema = auditoria;
-- Creación de la tabla de auditoría.
create table add_auditoria_dll (
add_id number generated always as identity (start with 1 increment by 1),
add_ts timestamp with time zone default systimestamp not null,
add_remoto_usu varchar2(50) null,
add_remoto_sistema_nom varchar2(100) null,
add_remoto_sistema_ip varchar2(15) not null,
add_remoto_sistema_term varchar2(100) null,
add_bd_usu_identif varchar2(128) not null, -- sys_context('USERENV','CURRENT_USER').
add_bd_usu_destino varchar2(128) not null, -- ora_login_user.
add_bd_obj_tipo varchar2(30) null, -- ora_dict_obj_type.
add_bd_obj_propietario varchar2(128) null, -- ora_dict_obj_owner.
add_bd_obj_nom varchar2(128) null, -- ora_dict_obj_name.
add_bd_evento varchar2(50) null, -- ora_sysevent.
add_bd_sql long null,
constraint add_pk primary key (add_id)
);
-- Creación de índices para un correcto rendimiento.
create index add_i_ts on add_auditoria_dll(add_ts);
create index add_i_remoto_usu on add_auditoria_dll(add_remoto_usu);
create index add_i_remoto_sistema_nom on add_auditoria_dll(add_remoto_sistema_nom);
create index add_i_remoto_sistema_ip on add_auditoria_dll(add_remoto_sistema_ip);
create index add_i_bd_usu_identif on add_auditoria_dll(add_bd_usu_identif);
create index add_i_bd_usu_destino on add_auditoria_dll(add_bd_usu_destino);
create index add_i_bd_obj_tipo on add_auditoria_dll(add_bd_obj_tipo);
create index add_i_bd_obj_propietario on add_auditoria_dll(add_bd_obj_propietario);
create index add_i_bd_obj_nom on add_auditoria_dll(add_bd_obj_nom);
create index add_i_bd_evento on add_auditoria_dll(add_bd_evento);
Explicamos el contenido de cada atributo:
add_ts timestamp
: Instante en el que se lanza la sentencia DDL.add_remoto_usu
: Usuario del sistema cliente desde el que se ha realizado la conexión a Oracle.add_remoto_sistema_nom
: Nombre del sistema cliente.add_remoto_sistema_ip
: IP del sistema cliente.add_remoto_sistema_term
: Terminal (si procede) del sistema cliente.add_bd_usu_identif
: Usuario de base de datos con el que ha iniciado sesión en Oracle.add_bd_usu_destino
: Usuario de base de datos destino de la conexión (si el inicio de sesión ha sido mediante usuario proxy8).add_bd_obj_tipo
: Tipo de objeto del esquema destino la sentencia DDL4.add_bd_obj_propietario
: Esquema (usuario de base de datos) destino la sentencia DDL4.add_bd_obj_nom
: Objeto del esquema destino la sentencia DDL4.add_bd_evento
: Operación lanzada (tipo de sentencia DDL4).add_bd_sql
: La sentencia SQL DDL4 completa.
Finalmente creamos el conjunto de disparadores, uno por esquema. Para agilizar el trabajo de creación (tratamos con aproximadamente 500 esquemas) creamos un bloque anónimo donde especificamos los criterios de creación de los disparadores:
declare
-- Conjunto de esquemas que se incluyen en la monitorización de DDL (mediante expresiones regulares).
-- «^.*$» indica que se incluyen todos los esquemas.
esquemas_incluidos long := '^.*$';
-- Esquema excluidos (los de sistema y otros especificados).
-- Si no divide en varias expresiones se produce error ORA-12733.
esquemas_excluidos1 long :=
'^(XS\$NULL|SYS|SYS.*|XDB|WMSYS|SPATIAL.*|OUTLN|ORD(SYS|PLUGINS|DATA)|ORACLE_OCM|OLAPSYS|' ||
'OJVMSYS|MDSYS|JSON_TEST|CTXSYS|APPQOSSYS|ANONYMOUS|LBACSYS|DBSWUSER|SCOTT|' ||
'APEX_PUBLIC_USER|APEX_050000|AUDSYS|DBSFWUSER|' ||
'REMOTE_SCHEDULER_AGENT|SI_INFORMTN_SCHEMA|MDDATA|APPQOSSYS)$'
;
esquemas_excluidos2 long :=
'^(USUARIO_EXCLUIDO_01|USUARIO_EXCLUIDO_02|USUARIO_EXCLUIDO_03|' ||
'TMP.*|TEST|.*CURSO.*|AUDITORIA|VMWARE.*)$'
;
-- Conjunto de esquemas excluidos (sobre los ya incluidos),
-- en la forma de «TIPO_DE_OBJETO~ESQUEMA~NOMBRE» (separados por «~»),
-- cada uno de estos en forma de expresión regular.
objetos_de_esquemas_excluidos long :=
'^(VIEW~ESQUEMA_01~.*|TABLE~ESQUEMA_02~TABLA_TEMPORAL.*|VIEW.*~ESQUEMA_03~V[0-9_]+)$'
;
-- Operacionaes excluidas a nivel global especificadas mediante expresiones regulares.
operaciones_excluidas long :=
'^(ANALYZE)$'
;
-- Roles de usuarios que no serán auditados ni limitados,
-- especificados en forma de expresión regular.
roles_excluidos long := '^(DBA|CDB_DBA|OEM_MONITOR|DV_MONITOR)$';
-- Esquemas en los que no se permiten modificaciones especificados mediante expresioens regulares.
esquemas_no_modificables_por_usuarios long :=
'^(ESQUEMA_04|ESQUEMA_05)$'
--'^(ninguno)$' -- Esta especificación no limita la modificación de ningún esquema.
;
-- Plantilla del disparador que se creará para cada esquema.
-- El contenido de la forma «{...}» será sustituído por la parte específica de cada esquema.
disparador long := '
create or replace trigger
auditoria.aud_ddl_{esquema}
before ddl on {esquema}.schema disable
declare
sql_dato long := '''';
sql_dato_lista dbms_standard.ora_name_list_t;
sql_dato_num_elementos binary_integer;
error varchar2(256) := null;
auditar char(1) := ''s'';
begin
-- Restricciones de ejecución.
if ora_dict_obj_type = ''TRIGGER'' and ora_dict_obj_name = ''AUD_INSPECCION_DDL'' then
error := ''No está permitido DDL sobre el disparador AUD_INSPECCION_DDL.'';
end if;
{esquema_no_modificable}
if error is not null then
raise_application_error(-20000, ''ERROR: '' || error);
end if;
{objeto_excluido}
-- Si se trata de operación excluida, se indica.
if regexp_like(ora_sysevent, ''' || operaciones_excluidas || ''') then
auditar := ''n'';
end if;
-- Registro de la instrucción.
if auditar = ''s'' then
sql_dato_num_elementos := ora_sql_txt(sql_dato_lista);
for sql_dato_elemento in 1 .. sql_dato_num_elementos
loop
sql_dato := sql_dato || sql_dato_lista(sql_dato_elemento);
end loop;
insert into auditoria.add_auditoria_dll (
add_remoto_usu,
add_remoto_sistema_nom,
add_remoto_sistema_ip,
add_remoto_sistema_term,
add_bd_usu_identif,
add_bd_usu_destino,
add_bd_obj_tipo,
add_bd_obj_propietario,
add_bd_obj_nom,
add_bd_evento,
add_bd_sql
) values (
sys_context(''USERENV'',''OS_USER''),
sys_context(''USERENV'',''HOST''),
sys_context(''USERENV'',''IP_ADDRESS''),
sys_context(''USERENV'',''TERMINAL''),
sys_context(''USERENV'',''CURRENT_USER''),
ora_login_user,
ora_dict_obj_type,
ora_dict_obj_owner,
ora_dict_obj_name,
ora_sysevent,
sql_dato
);
-- No es preciso hacer commit por es implícito a la instrucción ddl.
-- commit;
end if;
-- Si se produce un error, debe mostrarse (o no, según decisión del DBA).
--exception
-- when others then
-- null;
end;
'
;
-- Código para el disparador en caso de que el esquema no sea modificable.
ddl_esquema_no_modificable long := '
-- Este usaurio no puede modificar objectos de su esquema.
error :=
''Por seguridad e implicaciones con otros esquemas/aplicaciones, no está permitido '' ||
''la modificación de objetos de este esquema por usuarios.''
;
';
-- Código para el disparador en caso de que el objeto esté excluido de la auditoría.
ddl_objeto_excluido long := '
-- Objectos excluidos.
if regexp_like(ora_dict_obj_type || ''~'' || ora_dict_obj_owner || ''~'' || ora_dict_obj_name, ''' ||
objetos_de_esquemas_excluidos || ''') then
auditar := ''n'';
end if;
';
s long;
begin
dbms_output.enable(null);
-- Eliminación de disparadores de auditoría previos (todos con la intención de recrear todo).
dbms_output.put_line('Eliminación de disparadores.');
for d in (select * from dba_objects where owner = 'AUDITORIA' and object_type = 'TRIGGER' and object_name like 'AUD_DDL_%') loop
dbms_output.put_line(' eliminando: ' || d.object_name);
execute immediate 'drop trigger auditoria."' || d.object_name || '"';
end loop;
-- Otorgamiento de permisos a cada esquema auditado para la inserción de registros
-- de auditoría en la tabla de auditoría y creación de disparador para cada esquema auditado.
dbms_output.put_line('Otorgando permisos para escritura de auditorías y creando disparadores.');
for u in (
-- Selección de esquemas que cumplan con la especificación de inclusión/exclusión.
select distinct u.username
from dba_users u
where
regexp_like(u.username, esquemas_incluidos) and
(not (
regexp_like(u.username, esquemas_excluidos1) or
regexp_like(u.username, esquemas_excluidos2)
)
) and
u.username not in (
select ug.grantee
from dba_role_privs ug
where regexp_like(ug.granted_role, roles_excluidos)
)
order by u.username
) loop
dbms_output.put_line(u.username);
-- Otorgamiento del permiso de inserción.
execute immediate 'grant insert on auditoria.add_auditoria_dll to ' || u.username;
dbms_output.put_line(' Permiso otorgado.');
-- Generación del texto del disparador. Se sustituyen los textos específicos del
-- esquema en la plantilla del disparador.
s := disparador;
s := replace(s, '{esquema}', u.username);
if regexp_like(u.username, esquemas_no_modificables_por_usuarios) then
s := replace(s, '{esquema_no_modificable}', ddl_esquema_no_modificable);
else
s := replace(s, '{esquema_no_modificable}', '');
end if;
if regexp_like(
u.username,
'^(' ||
regexp_replace(
regexp_replace(
objetos_de_esquemas_excluidos, '([^~]*)~([^~]*)~([^~]*)', '\2|', 1, 0
),
'^(.*).$', '\1'
) ||
')$'
) then
dbms_output.put_line(' Añadiendo exclusión de objetos para este esquema.');
s := replace(s, '{objeto_excluido}', ddl_objeto_excluido);
else
s := replace(s, '{objeto_excluido}', '');
end if;
--dbms_output.put_line(s);
-- Creación del disparador (deshabilitado por si hay errores).
begin
execute immediate s;
exception
when others then null;
end;
dbms_output.put_line(' Disparador auditoria.aud_ddl_' || u.username || ' creado deshabilitado.');
-- Si se llega aquí, todo es correcto. Habilitación del disparador.
execute immediate 'alter trigger auditoria.aud_ddl_' || u.username || ' enable';
dbms_output.put_line(' Disparador habilitado.');
dbms_output.put_line('');
end loop;
end;
Con este bloque anónimo:
- limpiamos todos los disparadores previos que pudieran existir;
- especificamos los esquemas a auditar y a restrigir;
- creamos los correspondientes disparadores, almacenados en el esquema
auditoria
, evitando modificar el esquema auditado.
Guardaremos las trazas de auditoría en auditoria.add_auditoria_dll
:
> select * from auditoria.add_auditoria_dll
# | ADD_ID | ADD_TS | ADD_REMOTO_USU | ADD_REMOTO_SISTEMA_NOM | ADD_REMOTO_SISTEMA_IP | ADD_REMOTO_SISTEMA_TERM | ADD_BD_USU_IDENTIF | ADD_BD_USU_DESTINO | ADD_BD_OBJ_TIPO | ADD_BD_OBJ_PROPIETARIO | ADD_BD_OBJ_NOM | ADD_BD_EVENTO | ADD_BD_SQL |
-----+--------+-------------------------------+----------------+-------------------------+-----------------------+-------------------------+--------------------+--------------------+-----------------+------------------------+--------------------------------+---------------+-------------------------------------------------------------------------------------------------------+
1 | 641 | 2021-02-25 12:17:48.501 +0100 | usuario_01 | SISTEMA_01 | ***(omitida)*** | unknown | AUDITORIA | ESQUEMA_12 | TABLE | ESQUEMA_12 | TABLA_01 | ALTER | ALTER TABLE TABLA_01 ADD (ATRIB_01 CHAR ) |
2 | 642 | 2021-02-25 12:17:48.696 +0100 | usuario_01 | SISTEMA_01 | ***(omitida)*** | unknown | AUDITORIA | ESQUEMA_12 | TABLE | ESQUEMA_12 | TABLA_01 | ALTER | ALTER TABLE TABLA_01 ADD (ATRIB_02 TIMESTAMP ) |
...
Conclusión
La solución propuesta es más util que el sistema nativo de auditoría fina de Oracle9. Este paquete va incluido en la versión empresarial pero es opcional en la versión estándar10 y tiene costes de licencia extras. Además la auditoría fina de Oracle no limita la ejecución de sentencias DDL.
Nuestra solución apenas produce carga en el sistema, es flexible respecto a lo que se desea auditar y permite limitar las sentencias SQL DDL.
La solución fue adoptada en la corporación cumpliendo expectativas y requerimientos.
-
Cáculo relacional: Cálculo relacional. ↩
-
Un disparador, conocido también como trigger, es un procedimiento que se ejecuta en el lado del servidor de base de datos cuando se produce un evento concreto como puede ser la actualización de un conjunto de datos o de definición de esquemas. Entra dentro del conjunto de lógica que se ejecuta dentro del sistema gestor de base de datos llamada PL (Procedural Language).
Más información: Trigger base de datos. ↩ -
DDL es e acrónimo de «Data Definition Language», y es el subconjunto de instrucciones SQL encargada de modificar los esquemas de base datos.
Más información sobre qué es DDL: Lenguaje de definición de datos.
Más información acerca de las sentencias DDL de Oracle 19: Data Definition Language (DDL) Statements . ↩ -
Documentación sobre disparador por sentencia DDL de Oracle 19 ddl_event. ↩↩↩↩↩↩
-
Atributos creados por Oracle en el momento de la ejecución de un disparador por evento DDL: Event Attribute Functions. ↩
-
Función sys_context para obtener información del contexto de la conexión a Oracle por el usuario. ↩
-
Sentencia create tablespace. ↩
-
Información acerca de la autenticación mediante usuarios proxy en About Proxy Authentication. ↩
-
Sistema de auditoría fina de Oracle: Part VI Monitoring Database Activity with Auditing. ↩
-
Paquete opcional de auditoría fina de Oracle y su inclusión en licencias de Oracle Database: License Cost, Permitted Features, Options and Management Packs in Oracle 19C (Doc ID 2666967.1) y Differences Between Enterprise, Standard and Standard One Editions on Oracle 11.2 (Doc ID 1084132.1). ↩