您好,登錄后才能下訂單哦!
一個貌似簡單卻足以摧毀你整個應用的問題
絕對不是危言聳聽,直接點開大圖來看看,如果你看完覺得這個問題并無所謂而輕易放過,那么問題背后隱藏的巨大的風險就這樣被你忽略了;這個問題就是這樣,一個不經意的小環節,能使PLSQL Devloper出現問題,同樣,也足夠摧毀你整個應用!
01
背景介紹
這是在某客戶的生產數據中心遇到的問題,該數據中心有專門的操作控制中心,備有大量的操作終端,用于統一維護數據中心的系統。
某天,客戶DBA提問:應用維護團隊使用操作終端上的PLSQL developer連入他們的某個用戶查詢數據時會報錯(圖1),報錯后點擊ok按鈕PLSQL Developer就死掉了,但是如果使用sqlplus的方式登錄上去做操作并不受影響,而且如果連入的是其他用戶則不會出現這樣的問題;
作為應用維護團隊,經常會要查一些生產數據,甚至生成一些報表,那么各終端上的PLSQL Developer是他們的唯一能直連數據庫的工具了,PLSQL Developer不能用,對他們來說影響還是蠻大的;從問題本身看,數據庫的正常運行顯然沒有受到影響,但是對于應用維護來說,該庫內單獨某一用戶無法使用PLSQL Dev來操作,即認為是數據庫方面的問題。
有客戶不能解決的問題,我們當然義不容辭!披上戰袍,上場!!
看到這樣的問題,我不禁記起前段時間出現的互聯網上下載的PLSQL Dev使用后導致ORACLE數據庫異常,以及最近出現的互聯網上下載的ORACLE軟件出現的數據庫在運行一段時間后出現異常的情況,這里提醒各位讀者,盡量從官方網站,正規渠道下載相關軟件安裝介質,避免給不法分子可乘之機。當然,我們這里肯定不是遇到了這個問題。
02
問題擺在眼前,這是哪一方面的問題呢? 首先需要總結一些基本的現象:
拿到iptXXX用戶的密碼,我們很輕易地重現問題,可以知道,在“SQL窗口”中我們發現不管是查詢表/視圖都會出現上述問題;
再使用其他用戶,則不存在這方面的問題;
查詢數據庫整體,無明顯的失效對象;
從報錯"XXX must be declared"結合經驗來看,基本定位為權限問題;
另外,我們還需要考慮這里問題的直接導火索,直接使用sqlplus登錄做查詢沒有問題,而使用PLSQL Developer存在問題,那么PLSQL Developer在查詢的時候做了什么額外的操作呢?要定位這個倒是很簡單,通過相關會話的抓取其執行過的SQL我們就會看到下面語句:
begin
if :enable = 0 then
sys.dbms_output.disable;
else
sys.dbms_output.enable(:size);
end if;
end;
接下來,我們就試著在sqlplus命令窗口中執行相關過程:
而且,以上述方式執行其他的sys用戶下各種包的其他存儲過程,也存在同樣問題; 對比之下,登錄其他用戶執行上述存儲過程,則完全無問題。
對!沒錯!問題就是在調用sys用戶的包上面!
03
詭異的權限
畢竟iptXXX用戶是普通用戶,sys用戶的包豈能說調用就調用,先來看權限:
SQL>select privilege from dba_sys_privs where grantee='IPTXXX';
PRIVILEGE
----------------------------------------
CREATE VIEW
SELECTANY DICTIONARY
CREATE SYNONYM
UNLIMITED TABLESPACE
SQL> select GRANTED_ROLE from dba_role_privs wheregrantee='IPTXXX';
GRANTED_ROLE
------------------------------
RESOURCE
CONNECT
SQL> select role, privilege from role_sys_privs where privilege like '%EXECUTE%PROCEDURE%';
ROLE PRIVILEGE
------------------------------ ----------------------------------------
DBA EXECUTE ANY PROCEDURE
IMP_FULL_DATABASE EXECUTE ANY PROCEDURE
EXP_FULL_DATABASE EXECUTE ANY PROCEDURE
可以看到iptXXX用戶的權限看上去沒有什么異常,甚至比普通用戶的權限還要多那么一些些,還給了一個額外的查看數據字典的權限,查詢也沒有問題;
而同時,我們簡單的使用DESC命令去查看dbms_output包的所有過程/函數,是沒有問題的! 另外,以個人操作習慣,我們通常在調用sys用戶的包時,習慣上似乎很少加"sys."前綴的,比如我們在普通用戶收集自己表的統計信息,通常執行的是exec dbms_stats.gather_table_stats,那這里難道連統計信息收集都做不了了嗎?
這樣,就又多了一分詭異了,從結果上看, 帶不帶sys.前綴還有所不同了! sys.dbms_output和dbms_output是什么關系呢?以前我們經常會調用各種sys用戶的包,倒是并沒有考慮過這個問題,不過這里我們遇到的問題,那就順手了解了解吧;
SQL>selectobject_name,object_type,owner from dba_objects where object_name='DBMS_OUTPUT';
DBMS_OUTPUT PACKAGE SYS
DBMS_OUTPUT PACKAGE BODY SYS
DBMS_OUTPUT SYNONYM PUBLIC
SQL> select table_owner,table_name,owner fromdba_SYNONYMs where SYNONYM_NAME='DBMS_OUTPUT';
SYS DBMS_OUTPUT PUBLIC
原來,對于sys用戶的包來說,在public下都有對應的同義詞;
這樣,問題就又奇怪了,DBMS_OUTPUT本是sys下的一個對象,使用PUBLIC的同義詞可以訪問,而在加了“sys.”前綴后卻無法訪問,這個意味著什么呢?看起來好像不是簡單的權限問題了,難道是這個系統配置了什么特殊的安全策略,不允許該普通用戶直接調用sys用戶的包,而只能通過public的同義詞么?
不解!!
04
分析的過程中,客戶DBA又提示到,你看PLSQL Developer"命令窗口"做查詢怎么就沒事呢?
對此,我先是隨口說了一句,那顯然是因為它沒有調用sys用戶的包做操作啊,然而話說出來總覺說服不了自己,于是簡單跟蹤核查了一下,還真不是,在"命令窗口"也是有調用的;調用的語句如下:
begin :id :=sys.dbms_transaction.local_transaction_id; end;
顯然,這里是調用成功了的!這是什么情況,難道dbms_transaction包是例外?并沒有,我們再驗證一下:
那這里我們來考慮一下區別在哪?顯然,dbms_transaction.commit是一個procedure,而 dbms_transaction. local_transaction_id是一個function;如此,推而廣之再驗證,確實發現sys用戶的函數是可以被調用的,而procedure無法被帶前綴的調用;
似乎又進一步驗證了相關安全策略的問題,難道安全策略中還允許只能調用function,不能調用procedure? 腦子里一團漿糊!
05
各位看官似乎也要看亂了,問題到了這里,我們不妨來歸納總結一些,現在
我們到底遇到了什么問題:
1.desc dbms_output/desc sys.dbms_output都是沒有問題的
2.exec dbms_output.disable是沒有問題的(不帶sys.前綴)
3.exec sys.dbms_output.disable是有問題的(procedure,帶sys.前綴)
4.調用sys.dbms_transaction.local_transaction是沒有問題的(function,帶sys.前綴)
5.iptXXX用戶權限沒有看到明顯的漏洞(對于一個package來說,權限還會分procedure和functioin 么?)
6.懷疑存在針對sys用戶包的存儲過程調用的特殊策略
原本以為很簡單的問題,把所有現象列出來之后,我們才發現,問題似乎比我們想象的要復雜,最關鍵的是,找不到頭緒?而且,生產環境的問題,容不得大家隨意測試!各位看官,到這里,如果是你,你會如何判斷,或者如何走向下一步呢?不如我們來思考思考。。。
06
方法總會有的
問題似乎查不下去了,那我們就需要反思一番,是否我們一開始把問題定位的太簡單,就是權限問題,導致其影響到我們的判斷,最終把我們帶到了死胡同?我們何不抽離出來,換一種思考方式,擺脫我們的定式思維!
首先,PLS-00302/ORA-6500,就是一個報錯而已,雖然我們覺得就是權限的錯誤,我們暫不考慮;對于一個錯誤來說,那我們不妨來先對即產生錯誤的進程做個errorstack跟蹤:
SQL>oradebug setospid 3162340
Oraclepid: 55, Unix process pid: 3162340, image: oracle@XXXX(TNS V1-V3)
SQL>oradebug event 6550 trace name errorstack forever,level 12
Statement processed.
然后,拿到errorstack的trace文件大致是這樣的:
然而,事情總沒有我們想象的那么順利,給你一個trace文件,能一眼看到問題所在嗎?并不能!errorstack的事件大家似乎都會設置,那能不能讀就是另外一回事了,像上面的errorstack的設置,顯然,只是在報錯的時候做一個processdump而已,dump出來的內容非常之多,要從中精確提取我們所需要的信息,很難!特別是在問題方向都沒有的情況下;
errorstack的trace就先放著吧,也許一會兒用的上。
再換一個思路,執行的是sys用戶下的包,終歸是要調用sql語句的吧,那我們再來看看到底執行了什么SQL呢?我們不妨來做個10046事件進行跟蹤!
SQL>oradebug setospid 4657324
Oracle pid:67, Unix process pid: 4657324, image: oracle@XXXX(TNS V1-V3)
SQL>oradebug event 10046 trace name context forever,level 12
Statement processed.
設置事件后,在相應的會話上調用sys.dbms_output.disable;抓出來的trace文件是這樣的:
沒錯,就這么多,一點也不多,其中有一個等待事件"library cache lock”,通過核查與該等待相關的對象是dbms_standard包,這個包同樣是sys用戶的,也并無特別之處,同樣在iptXXX用戶下被調用會存在問題;
為什么10046抓取出來的trace會這么少呢,那看來,一執行就報錯了,壓根就沒有執行下去的機會?
這樣查下來,似乎想要跳出權限的死胡同,然而,卻也并沒有找到問題的方向!
還有什么好方法呢?
。
。
。
。
07
再換個思路
問題到這里,似乎是怎么也查不下去了,MOS上的搜索可能是因為問題都不明確,只能使用PLS-00302/ORA-6500等字眼去搜索,并沒有能提供幫助的文章/bug;期間各種生產環境范圍內允許的嘗試也做了,似乎還是沒有什么進展;
看來,找不到別的更好的方法,那么,我們需要反思:
我們剛剛的方法存在問題嗎?
1.errorstack抓取的trace到底能不能提供幫助呢?
答:顯然應該是有的,但是確實讀起來困難,如果我們并不那么熟悉其trace結構,那么此處不宜久留。
2.10046對于這樣的語句是否能抓到其調用的SQL呢?
答:不應該,在我的印象中10046是能抓到遞歸調用的sql的,而這里卻沒有抓到一條SQL,看起來不合理。
看起來,回歸到10046事件上是一個不錯的選擇,而且,我們確認,使用10046跟蹤sys.dbms_transaction.local_transaction時,是有許多遞歸SQL的調用的,將其中的sql逐一閱讀,似乎也沒有得到想要的信息;但是,這里,卻給了我一個小小的啟發:
對比!
沒錯,前面核查問題,一直因為在生產環境,沒有可以隨手對比測試的環境,導致將目光死死的盯在了問題本身上,缺乏對比;開拓新思路,我們如何將對比進行下去呢?
對于其他用戶而言,調用sys用戶的存儲過程是沒有任何問題的,那我們不妨來看看正確的調用過程中都做了些什么?總不至于也一條SQL都不執行吧!再次針對其他用戶(假設叫TEST用戶)做10046跟蹤,然后成功調用sys用戶包下的存儲過程,果然能抓到一些SQL,但還好不算多,而且映入眼簾的首先是這樣一條SQL:
我們不妨來試著一條一條讀下來,逐一對比試試看;這里這個語句,有三個參數,49,‘SYS’,1;在生產環境里核查:
SQL>select obj#,type#,ctime,mtime,stime,status,dataobj#,flags,oid$, spare1, spare2 from obj$ where owner#=49
and name='SYS' and namespace=1 and remoteowner is null and linkname is null and subname is null;
no rows selected
那么,怎么對比呢?顯然,49是我調用存儲過程的用戶ID(TEST用戶),這里如果是iptXXX用戶執行呢?
SQL>select user_id from dba_users where username='IPTXXX';
USER_ID
----------
34
SQL>select obj#,type#,ctime,mtime,stime,status,dataobj#,flags,oid$, spare1, spare2from obj$ where owner#=34 and name='SYS'and namespace=1 and remoteowner is null and linkname is null and subname isnull;
OBJ# TYPE# CTIME MTIME STIME STATUS
-------------------- ------------ ------------ ------------ ----------
DATAOBJ# FLAGS OID$ SPARE1 SPARE2
-------------------- -------------------------------- ---------- ----------
77574 2 19-JUN-09 07-JUL-17 19-JUN-09 1
77574 0 6 1
其實,如果是有經驗的DBA,應該就能看到問題的所在了;再加一步,所有人都會豁然開朗了:
SQL> Select object_name,object_type,owner from dba_objects where object_id=77574 ;
OBJECT_NAME OBJECT_TYPE OWNER
--------------------------------------------------------------------
SYS TABLE IPTXXX
沒錯,事情就是這樣的,查了大半天的問題查到這里,我感覺我的智商受到了一萬點的侮辱!!我就不知道有一句“XXX”不知當講不當講了,不禁感慨,這都什么樣的應用設計,什么樣的數據庫設計啊?原來,
在IPTXXX用戶下,居然有一個名字叫SYS的表
!!!原來真相簡單到令人發指!!!
08
反思!反思!反思!
問題查出來之后再看往往簡單,如果只看到這個結果,大家也許并不會有什么太多收獲;對此,我在查完這個問題之后,花了將近一天的時間對這個問題的處理過程、思路以及中間遇到的問題進行了深刻的反思,我才意識到這絕對是值得分享的一個案例。
反思一:
我們來看整個問題的過程;用戶IPTXXX上存在SYS對象,導致其調用SYS用戶下的包的存儲過程時,出現類似權限不足的錯誤;現在,對我們造成的影響是PLSQL Developer無法正常執行查詢;
那么,在其他更復雜的業務環境中,比如,某些黑盒子應用模塊中也存在著類似PLSQL Developer的這種調用,而在開發過程中基于這些黑盒子模塊進行二次開發,然后在SCHEMA里增加了SYS表,那么這個時候是足以使整個應用無法正常運行的
;通常,作為DBA我們會認為,ORACLE會很好的保留關鍵字避免被錯誤使用,然而,從這個CASE來看并沒有(
ORACLE的保留關鍵字在V$RESERVED_WORDS中可以找到
);
那么我們這里就要給廣大應用設計/開發人員一個提醒,在對象的設計過程中,避免使用類似SYS的關鍵字!同時,對于應用維護/DBA來說,我們就需要對這種情況進行上線前的檢查;對于各種SQL審核/應用審核的工具開發廠商來說,我們就需要將這種檢查加到我們的工具里;
反思二:
對于這個問題的處理過程而言,真的有那么復雜嗎?在權限問題上糾結了那么久:
我們顯然可以看到,對于DBMS_LOCK包,我們沒有調用權限時,數據庫拋出的錯是PLS-00201,而我們遇到的錯實際上是PLS-00302,其實在面對不熟悉的報錯,如果要確認其錯誤代碼的意義,可以先通過模擬報錯的形式來對比確認;
反思三
:
如果,我們仔細核查各個函數的意義,我們可以看到kkxsem意義函數的實際上為KKX SEMantic phase,即語義錯誤;確認了這個函數之后,我們即可確認該問題并非權限問題,避免走入誤區!對于callstack,我們還可以給予更多的關注,對于處理問題會有很大的幫助。
反思四:
10046事件真的不能跟蹤所有的SQL語句嗎?
不是這樣的,10046事件是能跟蹤到所有執行的SQL語句的!但是對于這里而言,執行的exec sys.dbms_output.disable()語句本身語義錯誤,我們試圖跟蹤的是該語句執行過程中產生的遞歸SQL;這里我們測試可以得出,在初次解析"
exec sys.dbms_output.disable()"語句的過程中,我們會需要load一些數據到library cache中,這個過程是需要執行遞歸SQL來實現,而在多次執行后,
"exec sys.dbms_output.disable()"的相關cursor已經緩存到library cache lock中,將不會再執行遞歸SQL,所以我們沒有能追蹤到;
反思五:這算不算一個bug呢?
是不是可以認為ORACLE忘記將SYS作為保留字而把這個問題當成bug?這里我們不做結論。
09
總結
對于這個CASE,如果還沒有了解到其結論的巨大危害性,請再仔細閱讀“反思一”部分,反正老K反思完,第一件事就是給我們的客戶提建議檢查所有的系統是否存在這樣類似的表設計。
從整個CASE看,最后得出的結論似乎很簡單,但是我們能從中理解到,一個糟糕的應用設計,足以將整個數據庫上的應用變得不可用;所以,一個好的設計才是一套應用系統正常運行的根本;
另外,對于任何一個CASE而言,我們收獲的不應該只是簡單的分析結果,對分析過程的反思與剖析往往比單純的結果更有意義;這也正是我們對于這個問題的分享所在!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。