Първо, мисля, че начинът да се подходи към „нелоялните администратори“ е чрез комбинация от одитната пътека на Oracle и Сейф за база данни функции.
Въпреки това, ето какво мога да опитам:
1) Създайте персонализирана ODCI агрегатна функция за изчисляване на хеш от множество редове като агрегат. 2) Създайте VIRTUAL NOT NULL
колона в таблицата, която беше SHA хеш на всички колони в таблицата -- или всички тези, които искате да защитите. Ще държите това наоколо през цялото време -- основно заменяйки малко insert/update/delete
производителност в замяна, за да можете да изчислявате хешовете по-бързо.3) Създайте неуникален индекс на тази виртуална колона4) SELECT my_aggregate_hash_function(virtual_hash_column) FROM my_table
за да получите резултатите.
Ето кода:
Създаване на обобщена функция за изчисляване на SHA хеш върху куп редове
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;
/
Създайте тестова таблица, с която да работите (пропускате това, тъй като имате истинската си таблица)
create table mattmsi as select * from mtl_system_items where rownum <= 200000;
Създайте хеш виртуална колона от данните на всеки ред. Уверете се, че е 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 ;
Създаване на индекс във виртуалната колона; по този начин можете да изчислите своя хеш с пълно сканиране на тесния индекс вместо пълно сканиране на масовата таблица
create index msi_compliance_hash_n1 on mattmsi (compliance_hash);
Съберете всичко заедно, за да изчислите своя хеш
SELECT matt_hash_aggregate(compliance_hash) from (select compliance_hash from mattmsi order by compliance_hash);
Няколко коментара:
- Мисля, че е важно да се използва хеш за изчисляване на съвкупността (вместо просто да се прави
SUM()
върху хешовете на ниво ред, тъй като нападателят може много лесно да фалшифицира правилната сума. - Не мисля, че можете (лесно?) да използвате паралелна заявка тъй като е важно редовете да бъдат подавани към агрегатната функция в последователен ред, в противен случай хеш стойността ще се промени.