回到人間,聊聊數(shù)據(jù)表誤刪的防止
經(jīng)過艱苦的跋涉,前天下午終于走出沒有信號、沒有人、沒有污染的阿布吉山區(qū)。走到九龍牧場的山脊的時候,遠(yuǎn)遠(yuǎn)可以看見公路和公路上偶爾駛過的汽車時,心情還是有點小激動的。
本次徒步五天四夜,實際上是5天,第一天下午從洗臉盆埡口出發(fā)只走了4公里就宿營了,最后一天13公里的下撤也只走了差不多五小時,總體來說還是比較輕松的一次徒步。走阿布吉措之前,我一直認(rèn)為這是一條兩星級左右的入門線路,因此本次除了三個大學(xué)生和準(zhǔn)大學(xué)生小朋友外,都是50多歲到60多歲的老朋友。
沒想到走下來,線路難度還是不低的,領(lǐng)隊說線路難度在3星到3星半之間,我覺得這個評價還是準(zhǔn)確的,有些事情,不親自去體驗一下,光是紙上談兵,會謬之千里的。十分高興的是在4417米的金柱埡口上居然能夠收到中國移動的信號,正好這個時候收到了兒子被北京化工大學(xué)化學(xué)與生物工程雙學(xué)位專業(yè)錄取的短信,還是很高興的。
一出山就收到不少消息,其中有一起數(shù)據(jù)庫被誤刪數(shù)據(jù),導(dǎo)致業(yè)務(wù)中斷的案例。這是我在7月份看到的第二個誤刪數(shù)據(jù)影響業(yè)務(wù)的案例了。誤刪數(shù)據(jù)現(xiàn)在已經(jīng)是對系統(tǒng)運(yùn)行安全較大的威脅了,今天出山后恢復(fù)寫公眾號的第一天,我們就來討論討論這個問題吧。
防止誤刪數(shù)據(jù)這個永恒的話題我是從20多年前就在關(guān)注了,1999年的時候一家銀行找到我們,讓我們幫助寫了一個C/S架構(gòu)的小工具,所有的數(shù)據(jù)庫維護(hù)操作都必須通過這個工具連到數(shù)據(jù)庫上去做,并且內(nèi)置了危險SQL的執(zhí)行確認(rèn)告警,以及刪除數(shù)據(jù)操作的審批等流程。工具很簡單,1個POWERBUILDER工程師花了不到一個月就完成了。這個工具一套賣幾十萬,居然也賣出去好幾套。
從防止誤操作的角度去看,可以從制度上去約束,制定嚴(yán)格的檢修流程,不過這種制度化的手段往往百密一疏,一次疏漏足以釀成大錯。因此純粹依靠制度是不行的。必須通過一些技術(shù)手段來防范。
技術(shù)手段分為外部防護(hù)和內(nèi)部防護(hù)兩種。外部防護(hù)可以通過數(shù)據(jù)庫防火墻和類似我們二十多年前做的數(shù)據(jù)庫運(yùn)維工具等方式來加強(qiáng)管理。對于誤操作能夠自動提醒甚至自動阻斷。對于核心業(yè)務(wù)數(shù)據(jù)的查詢和修改操作可以通過雙崗確認(rèn)甚至上級審批才能執(zhí)行(很多行業(yè)實際上對于重要變更操作都有這個要求,只是通過制度無法確保效果)。
僅僅采用外部防護(hù)還不夠完整。因為總會有高權(quán)限的DBA能夠繞過一切外部防護(hù),直接到數(shù)據(jù)庫服務(wù)器上用最高權(quán)限的賬號去操作數(shù)據(jù)庫。因此還需要有一些內(nèi)在防護(hù)的手段。20多年前,我們就幫很多客戶利用Oracle系統(tǒng)級觸發(fā)器構(gòu)建了一些數(shù)據(jù)庫防誤刪表的防護(hù)加固系統(tǒng),實際使用效果還是很好的。
文章的最后,我介紹一下在Oracle上實現(xiàn)數(shù)據(jù)保護(hù)的一個簡單的例子,在支持系統(tǒng)級觸發(fā)器的數(shù)據(jù)庫上,都可以采用類似的方法來實現(xiàn)。二十多年前,我們幫助不少客戶實施過類似的數(shù)據(jù)庫關(guān)鍵數(shù)據(jù)加固保護(hù),也利用這種小技巧賺了不少錢。
create table base$del_check(
tab_owner vachar2(100),
tab_name varchar2(100),
state number(10)
);
首先定義一張基礎(chǔ)表,用于需要保護(hù)的表的信息。這里我們創(chuàng)建了一張base$del_check的表。然后用下面的系統(tǒng)級觸發(fā)器來保護(hù)。我稍微做了簡化,沒有帶上Owner等信息。
CREATE OR REPLACE TRIGGER del_check
BEFORE DROP or truncate ON DATABASE
declare
vs_tab varchar2(100);
cursor cur_tab is
select LOWER(tab_name) from base$del_check
where stat=1;
BEGIN
open cur_tab;
loop
fetch cur_tab into vs_tab;
if cur_tab%notfound then
exit;
end if;
IF LOWER (ora_dict_obj_name ()) = vs_tab
THEN
raise_application_error (num => -20000,
msg => '你傻啊,這張表都敢刪?。。?!');
END IF;
end loop;
close cur_tab;
END;
上述工作做完后,只要是base$del_check中state為1的表,你要去做DROP/TRUNCATE等危險操作的時候,就可以直接被系統(tǒng)禁止了。大家有興趣的話可以繼續(xù)優(yōu)化這個腳本,并用于生產(chǎn)環(huán)境。