Área de Tecnologías de la Información y las Comunicaciones Aplicadas
Área de Tecnologías de la Información y las Comunicaciones Aplicadas Universidad de Murcia
Área de Tecnologías de la Información y las Comunicaciones Aplicadas
ATICA arrow MNCS arrow DBconnector arrow Oracle VPD (Virtual Private Database)
25.11.2017
 
 
Oracle VPD (Virtual Private Database) Imprimir E-mail

Oracle Virtual Private Database (VPD) permite definir (de forma nativa en la BD) una política de seguridad para el acceso a los datos de una tabla, simplemente creando una función que devolverá un predicado, que Oracle automáticamente aplicará siempre que se acceda a la tabla en cuestión, de modo que cada vez que la aplicación haga "select ... from mitabla ..." realmente estará haciendo "select ... from mitabla WHERE PREDICADO_RESULTANTE_DE_POLITICA_DE_ACCESO_DE_TABLA ...". De esta forma, podemos hacer que una misma sentencia sql devuelva diferentes filas en función del usuario (o la sesión) que la ejecute.

Puedo crear más de una política de seguridad sobre una misma tabla, de modo que se sumarán todos los predicados con AND.

Para cada política de seguridad que defina, puedo indicar con qué tipo de sentencias se activará (INSERT, UPDATE, DELETE y/o SELECT), pudiendo ser todas o sólo algunas o alguna de ellas.

Además puedo hacer que una política de seguridad se active sólo cuando se accede a una o varias columnas concretas de la tabla en cuestión.

La función que implementa una política de seguridad, en su lógica, puede usar la función del sistema "USER" (en el caso de que cada usuario de la aplicación se corresponda con un usuario de BD, caso de las aplicaciones C/S) o, también (si se trata de una aplicación web), puede usar el atributo CLIENT_IDENTIFIER del contexto USERENV, o bien crear un contexto y usar  "SYS_CONTEXT('nombre_de_contexto', 'nombre_de_atributo')", para generar un predicado diferente, en función del usuario (ya sea de BD o gestionado por la propia aplicación mediante el DNI, email, etc).

La solución anterior (CLIENT_IDENTIFIER o atributo en un nuevo CONTEXTO) es válida, en general e independientemente de la arquitectura de la aplicación (C/S o Web), para cualquier tipo de aplicación donde NO se identifique al usuario mediante el usuario de BD, sino que sea la aplicación quién gestiona la autenticación (mediante DNI, correo, etc).

La "magia" de Oracle VPD tiene un coste en términos de rendimiento, pues ahora cada vez que se accede a la tabla, Oracle tiene que ejecutar la política correspondiente. Por ello es muy importante, evaluar el impacto en el rendimiento que tiene la ejecución de la política de seguridad para cada tabla que la necesite, optimizando al máximo el código PL/SQL de las funciones correspondiente. Además, podemos definir las políticas como STATIC, de modo que se evaluarán sólo durante la compilación de la sentencia (y no en cada ejecución, que sería el caso de una política definida como DYNAMIC).

Ejemplos:

Vamos a ver cómo usar Oracle VPD a través de una serie de ejemplos. Los pasos básicos para configurar Oracle VPD sobre una tabla son muy simples:
  • Crear una función que genere el predicado que restringirá el acceso a la tabla, implementando la política de seguridad; con 2 parámetros de entrada (owner y table) y q devuelva un VARCHAR2 (el predicado):
create or replace function get_doctor_id
(p_schema_name in varchar2,
 p_table_name in varchar2) return varchar2
is
  l_doctor_id number;
begin
  select doctor_id
    into l_doctor_id
    from doctors
   where doctor_name = USER;
  return 'doctor_id = '||l_doctor_id;
end;
  • Crear y activar la política mendiante DBMS_RLS.ADD_POLICY, indicando la tabla en cuestión, así como la función que implementa la política, y en qué operaciones se activará dicha política (select, insert, update, delete):

begin
  dbms_rls.add_policy (
    object_schema => 'HOSPITAL',
    object_name => 'PATIENTS',
    policy_name => 'PATIENT_VIEW_POLICY',
    policy_function => 'GET_DOCTOR_ID',
    function_schema => 'HOSPITAL',
    statement_types => 'SELECT, INSERT, UPDATE, DELETE',
    update_check => true,
    enable => true);
end;

En el ejemplo anterior, hemos usado la función del sistema USER dentro de la función que define la política de seguridad (y que devuelve el predicado que Oracle aplicará cuando se acceda a la tabla en cuestión). En el caso de una aplicación web, donde el valor de USER siempre es el mismo, ya que el usuario de BD es siempre el del Datasource de la aplicación, se puede usar un atributo de un contexto, como por ejemplo:

create or replace function get_doctor_id
(p_schema_name in varchar2,
 p_table_name in varchar2) return varchar2
is
  l_doctor_id number;
begin
  if (p_schema_name = USER) then
    return null;
  end if;
  select doctor_id
    into l_doctor_id
    from doctors
   where doctor_name in (USER, sys_context('ctx_prueba','email'));
  return 'doctor_id = '||l_doctor_id;
end;

Para que ésto funcione, el usuario de BD del Datasource debe poder ejecutar el paquete que permite darle valor a los atributos del contexto, de modo que siempre antes de acceder a la tabla, y siguiendo con el ejemplo, se le dará valor al atributo "email":

SQL> exec pkg_ctx_prueba.name_value('email','micorreo@um.es');
PL/SQL procedure successfully completed.

Si por cualquier razón, tenemos que sustituir la función que implementa la política de seguridad por otra nueva (cambia el nombre), tendremos que borrar la política y crearla de nuevo indicando el nuevo nombre de la función:

begin
  dbms_rls.drop_policy (
    object_schema => 'HOSPITAL',
    object_name => 'PATIENTS',
    policy_name => 'PATIENT_VIEW_POLICY');
end;

begin
  dbms_rls.add_policy (
    object_schema => 'HOSPITAL',
    object_name => 'PATIENTS',
    policy_name => 'PATIENT_VIEW_POLICY',
    policy_function => 'GET_PATIENT_ID',
    function_schema => 'HOSPITAL',
    statement_types => 'SELECT, INSERT, UPDATE, DELETE',
    update_check => true,
    enable => true);
end;

También podemos restringir la activación de la política de seguridad a sólo una o varias columnas de la tabla, de modo que por ejemplo podamos hacer un "SELECT COUNT(*) ...", o acceder a columnas de la tabla que no contienen información sensible sin ningún problema. Siguiendo con nuestro ejemplo, si queremos restringir el acceso a la tabla de pacientes sólo cuando se acceda a las columnas que contienen el identificador de paciente, y el nº de la seguridad social:

begin
  dbms_rls.add_policy (
    object_schema => 'HOSPITAL',
    object_name => 'PATIENTS',
    policy_name => 'PATIENT_VIEW_POLICY',
    policy_function => 'GET_PATIENT_ID',
    function_schema => 'HOSPITAL',
    statement_types => 'SELECT, INSERT, UPDATE, DELETE',
    update_check => true,
    sec_relevant_cols => 'PATIENT_ID, SOCIAL_SEC_NO');
end;

Excepcionalmente se puede otorgar el privilegio de sistema "EXEMPT ACCESS POLICY" para que a un usuario concreto NO se le apliquen las políticas de seguridad de Oracle VPD.

Enlaces de interés:

Última modificación ( 25.05.2011 )
 
Área de Tecnologías de la Información y las Comunicaciones Aplicadas
Volver al incio del documento Volver al inicio del documento
Área de Tecnologías de la Información y las Comunicaciones Aplicadas