Oracle
 sql >> Base de données >  >> RDS >> Oracle

Le moyen le plus rapide de calculer le hachage d'une table entière

Tout d'abord, je pense que la façon d'aborder les "administrateurs malhonnêtes" consiste à combiner la piste d'audit d'Oracle et Database Vault fonctionnalités.

Cela dit, voici ce que je pourrais essayer :

1) Créez une fonction d'agrégation ODCI personnalisée pour calculer un hachage de plusieurs lignes en tant qu'agrégat.2) Créez un VIRTUAL NOT NULL colonne de la table qui était un hachage SHA de toutes les colonnes de la table - ou de toutes celles que vous vous souciez de protéger. Vous garderiez cela tout le temps - en gros, échangez quelques insert/update/delete performances en échange pour pouvoir calculer les hachages plus rapidement.3) Créez un index non unique sur cette colonne virtuelle4) SELECT my_aggregate_hash_function(virtual_hash_column) FROM my_table pour obtenir les résultats.

Voici le code :

Créer une fonction d'agrégation pour calculer un hachage SHA sur un groupe de lignes

CREATE OR REPLACE TYPE matt_hash_aggregate_impl AS OBJECT
(
  hash_value RAW(32000),
  CONSTRUCTOR FUNCTION matt_hash_aggregate_impl(SELF IN OUT NOCOPY matt_hash_aggregate_impl ) RETURN SELF AS RESULT,  
-- Called to initialize a new aggregation context
-- For analytic functions, the aggregation context of the *previous* window is passed in, so we only need to adjust as needed instead 
-- of creating the new aggregation context from scratch
  STATIC FUNCTION ODCIAggregateInitialize (sctx IN OUT matt_hash_aggregate_impl) RETURN NUMBER,
-- Called when a new data point is added to an aggregation context  
  MEMBER FUNCTION ODCIAggregateIterate (self IN OUT matt_hash_aggregate_impl, value IN raw ) RETURN NUMBER,
-- Called to return the computed aggragate from an aggregation context
  MEMBER FUNCTION ODCIAggregateTerminate (self IN matt_hash_aggregate_impl, returnValue OUT raw, flags IN NUMBER) RETURN NUMBER,
-- Called to merge to two aggregation contexts into one (e.g., merging results of parallel slaves) 
  MEMBER FUNCTION ODCIAggregateMerge (self IN OUT matt_hash_aggregate_impl, ctx2 IN matt_hash_aggregate_impl) RETURN NUMBER,
  -- ODCIAggregateDelete
  MEMBER FUNCTION ODCIAggregateDelete(self IN OUT matt_hash_aggregate_impl, value raw) RETURN NUMBER  
);

/

CREATE OR REPLACE TYPE BODY matt_hash_aggregate_impl IS

CONSTRUCTOR FUNCTION matt_hash_aggregate_impl(SELF IN OUT NOCOPY matt_hash_aggregate_impl ) RETURN SELF AS RESULT IS
BEGIN
  SELF.hash_value := null;
  RETURN;
END;


STATIC FUNCTION ODCIAggregateInitialize (sctx IN OUT matt_hash_aggregate_impl) RETURN NUMBER IS
BEGIN
  sctx := matt_hash_aggregate_impl ();
  RETURN ODCIConst.Success;
END;


MEMBER FUNCTION ODCIAggregateIterate (self IN OUT matt_hash_aggregate_impl, value IN raw ) RETURN NUMBER IS
BEGIN
  IF self.hash_value IS NULL THEN
    self.hash_value := dbms_crypto.hash(value, dbms_crypto.hash_sh1);
  ELSE 
      self.hash_value := dbms_crypto.hash(self.hash_value || value, dbms_crypto.hash_sh1);
  END IF;
  RETURN ODCIConst.Success;
END;

MEMBER FUNCTION ODCIAggregateTerminate (self IN matt_hash_aggregate_impl, returnValue OUT raw, flags IN NUMBER) RETURN NUMBER IS
BEGIN
  returnValue := dbms_crypto.hash(self.hash_value,dbms_crypto.hash_sh1);
  RETURN ODCIConst.Success;
END;

MEMBER FUNCTION ODCIAggregateMerge (self IN OUT matt_hash_aggregate_impl, ctx2 IN matt_hash_aggregate_impl) RETURN NUMBER IS
BEGIN
    self.hash_value := dbms_crypto.hash(self.hash_value || ctx2.hash_value, dbms_crypto.hash_sh1);
  RETURN ODCIConst.Success;
END;

-- ODCIAggregateDelete
MEMBER FUNCTION ODCIAggregateDelete(self IN OUT matt_hash_aggregate_impl, value raw) RETURN NUMBER IS
BEGIN
  raise_application_error(-20001, 'Invalid operation -- hash aggregate function does not support windowing!');
END;  

END;
/

CREATE OR REPLACE FUNCTION matt_hash_aggregate ( input raw) RETURN raw
PARALLEL_ENABLE AGGREGATE USING matt_hash_aggregate_impl;
/

Créez une table de test avec laquelle travailler (vous sautez ceci puisque vous avez votre vraie table)

create table mattmsi as select * from mtl_system_items where rownum <= 200000;

Créez un hachage de colonne virtuelle des données de chaque ligne. Assurez-vous qu'il est NOT NULL

alter table mattmsi add compliance_hash generated always as ( dbms_crypto.hash(to_clob(inventory_item_id || segment1 || last_update_date || created_by || description), 3 /*dbms_crypto.hash_sh1*/) ) VIRTUAL not null ;

Créez un index sur la colonne virtuelle ; de cette façon, vous pouvez calculer votre hachage avec une analyse complète de l'index étroit au lieu d'une analyse complète de la table grasse

create index msi_compliance_hash_n1 on mattmsi (compliance_hash);  

Mettez tout cela ensemble pour calculer votre hachage

SELECT matt_hash_aggregate(compliance_hash) from (select compliance_hash from mattmsi order by compliance_hash);

Quelques commentaires :

  1. Je pense qu'il est important d'utiliser un hachage pour calculer l'agrégat (plutôt que de simplement faire un SUM() sur les hachages au niveau des lignes, car un attaquant pourrait falsifier la bonne somme très facilement.
  2. Je ne pense pas que vous puissiez (facilement ?) utiliser la requête parallèle car il est important que les lignes soient transmises à la fonction d'agrégation dans un ordre cohérent, sinon la valeur de hachage changera.