oracle 密碼安全 (zt)

season0891發表於2009-08-26

密碼安全 
                                     日月明王
一,簡介
    思考密碼安全必須以HACKER的角度思考,要用必要的密碼策略老保證密碼不容易被猜到和定期被修改。但實際情況是很多ACCOUNT的密碼都是一樣的, 或者使用者名稱和密碼是一樣的,這些都不是一個好的DBA應該做的事情,可能導致一些HACKER透過多次試登陸來猜中密碼並獲取重要商業資料,甚至會導致數 據庫崩潰。
    先來了解密碼吧,密碼應該由字元開始,最長30個字元,大小寫不敏感。一個難猜的密碼必須由字元,數字,特殊字元組成,一定不能用字典裡能找到的單詞作為密碼,比如wq34($j9就是好的密碼。並且作業系統的安全比ORACLE的安全更重要。
    首先了解ORACLE的密碼並不區分大小寫,哪怕你加“”也是不區分的。
SQL> create user testuser identified by testuser;
User created.
SQL> select username,password from dba_users where username='TESTUSER'
  2  /
USERNAME
------------------------------------------------------------
PASSWORD
------------------------------------------------------------
TESTUSER
B222330EE300D65A

SQL> drop user testuser;
User dropped.
  1* create user testuser identified by "TestUser"
SQL> /
User created.
SQL> select username,password from dba_users where username='TESTUSER'
  2  /
USERNAME
------------------------------------------------------------
PASSWORD
------------------------------------------------------------
TESTUSER
B222330EE300D65A
     從上面可知,相同的字元,但大小寫不同,在ORACLE密碼裡是一樣的。
    還需要知道ORACLE密碼是由DEC加密演算法進行加密的。以16進位制儲存在SYS.USER$表中。
 
二,設定不可能設定的密碼
    我們可以透過一些途徑突破ORACLE的限制設定ORACLE密碼,應該瞭解密碼裡不能包含由CTRL+CHARACTER的字元組成。但我們可以透過“”來設定
SQL> create user testuser identified by j^Ljjjj
  2  /
create user testuser identified by j
                                    jjjj
                                     *
ERROR at line 1:
ORA-00922: missing or invalid option
SQL> create user testuser identified by "j^Ljjjj"
  2  /
User created.
SQL> connect testuser/j^Ljjjj
SP2-0306: Invalid option.
Usage: CONN[ECT] [logon] [AS {SYSDBA|SYSOPER}]
where   ::= [/][@] | /
SQL> connect testuser/"j^Ljjjj"
Connected.
   
   ORACLE還不許密碼以用其他字元開頭,我們還是可以透過“”來設定,比如
alter user testuser identified by '!impossible!'
                                  *
ERROR at line 1:
ORA-00988: missing or invalid password(s)
  1* alter user testuser identified by "!impossible!"
SQL> /
User altered.
SQL> connect testuser/!impossible!
Connected.
SQL> connect testuser/"!impossible!"
Connected.
 
三,對密碼的設定,修改進行跟蹤
     大家都聽過賊喊捉賊吧,那麼現在就開始先做一個能抓賊的賊(不難理解吧,嘿嘿)。
     首先要保證網路和OS的安全,不在這裡討論,那麼HACKER沒有得到ROOT,ORACLE OS的許可權,他們會怎麼做呢?他們會努力得到ORACLE的密碼,然後登陸,進行自己的操作,好的HACKER還會再清除自己的操作記錄,讓你感覺不到他的操作。
     一些HACKER會先得到密文‘B222330EE300D65A’,並且透過其他手段來獲得修改使用者密碼的許可權,修改使用者密碼,然後做HACKER想做 的事情,做完後再透過alter user username identified by values 'B222330EE300D65A'來複原到原來的密碼,而你卻不知道HACKER已經光顧了。這就需要我們記錄使用者對密碼所做的相關操作。
     首先建 立序列pass_seq和表 pass_thief,pass_thief只是用來測試,大家可以按需要建立其他欄位來收集其他資訊。pass_seq是用來產生序列插入 pass_thief表來判斷先後順序,也可以改成時間。pass_thief是用來記錄使用者名稱,操作,密碼的表。
SQL> create sequence pass_seq start with 1;
Sequence created.
SQL> create table pass_thief( 
  2  id number(6),
  3  username varchar2(30),
  4  value varchar(60)
  5  )
  6  /
Table created.
      做符合自己要求的FUNCTION,verify_function 函式最初是設定一些基本的密碼要求,比如不能和USERNAME同名,最少7個字元(8I前是4個字元),且必須包含至少一個字元,必須包含至少一個數 字,必須包含至少一個符號標點,必須至少三個字元是和原密碼不一樣,不能是如下單詞'welcome', 'database', 'account', 'user', 'password', 'oracle', 'computer', 'abcd'。但這些還不能滿足我們的需要,大家可以自己修改verify_function,使其要求更苛刻,因為HACKER對密碼的猜測一般是根據 ORACLE預設的要求來做的,所以,哪怕是一點點修改,只要是和原ORACLE要求不一致,就可能會給HACKER造成很大困難,這個FUNCTION 並不難,就不在這裡討論具體修改了。
     增加對密碼的跟蹤功能
     修改$ORACLE_HOME/rdbms/admin/utlpwdmg.sql,加一個匿名塊pwdsnoop,用來儲存username/password和其他資訊.
 Rem    NOTES
Rem      This file contains a function for minimum checking of password
Rem      complexity. This is more of a sample function that the customer
Rem      can use to develop the function for actual complexity checks that the
Rem
-- This script. sets the default password resource parameters
-- This script. needs to be run to enable the password features.
-- However the default resource parameters can be changed based
-- on the need.
-- A default password complexity function is also provided.
-- This function makes the minimum complexity checks like
-- the minimum length of the password, password not same as the
-- username, etc. The user may enhance this function according to
-- the need.
-- This function must be created in SYS schema.
-- connect sys/ as sysdba before running the script
CREATE OR REPLACE FUNCTION verify_function_test (
  username varchar2,
  password varchar2,
  old_password varchar2
) RETURN boolean
IS
  n boolean;
  m integer;
  differ integer;
  isdigit boolean;
  ischar  boolean;
  ispunct boolean;
  digitarray varchar2(20);
  punctarray varchar2(25);
  chararray varchar2(52);
 
  procedure pwdsnoop( username_in varchar2, value_in varchar2 )
  is
    pragma autonomous_transaction;
  begin
    insert into pass_thief (id,username, value) values (pass_seq.nextval, username_in, value_in);
    commit;
  end;
 
BEGIN
  digitarray:= '0123456789';
  chararray:= 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
  punctarray:='!"#$%&()``*+,-/:;<=>?_';
 
  pwdsnoop(username, '***********************************');
  pwdsnoop(username, 'NEW PASSWORD: '||password);
  -- Check if the password is same as the username
  pwdsnoop(username, 'check for username=password');
  IF NLS_LOWER(password) = NLS_LOWER(username) THEN
    raise_application_error(-20001, 'Password same as or similar to user');
  END IF;
 
  -- Check for the minimum length of the password
  pwdsnoop(username, 'check for password length');
  IF length(password) < 7 THEN
    raise_application_error(-20002, 'Password length less than 7');
  END IF;
 
  -- Check if the password is too simple. A dictionary of words may be
  -- maintained and a check may be made so as not to allow the words
  -- that are too simple for the password.
  pwdsnoop(username, 'check for easy passwords');
  IF NLS_LOWER(password) IN ('welcome', 'database', 'account', 'user',
                             'password', 'oracle', 'computer', 'abcd') THEN
    raise_application_error(-20002, 'Password too simple');
  END IF;
 
  -- Check if the password contains at least one letter, one digit and one
  -- punctuation mark.
  -- 1. Check for the digit
  isdigit:=FALSE;
  m := length(password);
  pwdsnoop(username, 'checking for digits in password');
  FOR i IN 1..10 LOOP
    FOR j IN 1..m LOOP
      IF substr(password,j,1) = substr(digitarray,i,1) THEN
        isdigit:=TRUE;
        GOTO findchar;
      END IF;
    END LOOP;
  END LOOP;
  IF isdigit = FALSE THEN
    raise_application_error(-20003,
      'Password should contain at least one digit, one character and one punctuation');
  END IF;
  -- 2. Check for the character
  <>
  ischar:=FALSE;
  pwdsnoop(username, 'checking for char in password');
  FOR i IN 1..length(chararray) LOOP
    FOR j IN 1..m LOOP
      IF substr(password,j,1) = substr(chararray,i,1) THEN
        ischar:=TRUE;
        GOTO findpunct;
      END IF;
    END LOOP;
  END LOOP;
  IF ischar = FALSE THEN
    raise_application_error(-20003,
       'Password should contain at least one digit, one character and one punctuation');
  END IF;
  -- 3. Check for the punctuation
  <>
  ispunct:=FALSE;
  pwdsnoop(username, 'checking for punctuation in password');
  FOR i IN 1..length(punctarray) LOOP
    FOR j IN 1..m LOOP
      IF substr(password,j,1) = substr(punctarray,i,1) THEN
        ispunct:=TRUE;
        GOTO endsearch;
      END IF;
    END LOOP;
  END LOOP;
  IF ispunct = FALSE THEN
    raise_application_error(-20003,
      'Password should contain at least one digit, one character and one punctuation');
  END IF;
 
  <>
  -- Check if the password differs from the previous password by at least
  -- 3 letters
  pwdsnoop(username, 'checking for password != old_password');
  IF old_password IS NOT NULL THEN
    pwdsnoop(username, 'old_password is NOT null');
    pwdsnoop(username, 'OLD PASSWORD: '||old_password);
    differ := length(old_password) - length(password);
 
    IF abs(differ) < 3 THEN
      IF length(password) < length(old_password) THEN
        m := length(password);
      ELSE
        m := length(old_password);
      END IF;
 
      differ := abs(differ);
      FOR i IN 1..m LOOP
        IF substr(password,i,1) != substr(old_password,i,1) THEN
          differ := differ + 1;
        END IF;
      END LOOP;
 
      IF differ < 3 THEN
        raise_application_error(-20004,
          'Password should differ by at least 3 characters');
      END IF;
    END IF;
  ELSE
    pwdsnoop(username, 'old_password is null');
  END IF;
  -- Everything is fine; return TRUE ;
  pwdsnoop(username, 'returned TRUE');
  RETURN(TRUE);
END;
/
Function created.
 #注 意:以上判斷是否包含字元,數字,或標點符號時可以改成If Length(Replace(password,'!"#$%&()``*+,-/:;?_')) = Length(password) Then raise_application_error(-20003, 'Password should contain at least one digit, one character and one punctuation'); End If;
,如果改成這樣的話效率應該更高,因為減少了多次迴圈。
     用剛做的verify_function_test做一個PROFILE hackprof.
SQL> create profile hackprof limit
  2  PASSWORD_LIFE_TIME 60
  3  PASSWORD_GRACE_TIME 10
  4  PASSWORD_REUSE_TIME 1800
  5  PASSWORD_REUSE_MAX UNLIMITED
  6  FAILED_LOGIN_ATTEMPTS 3
  7  PASSWORD_LOCK_TIME 1/1440
  8  PASSWORD_VERIFY_FUNCTION verify_function_test
  9  ;
Profile created.
     (具體的PROFILE引數含義請自行查詢。)
     現在準備就緒,自己當回被抓的賊吧.
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
     首先建立一個使用者但不用hackprof作為自己的PFORILE
SQL> create user testuser identified by jjjjj default tablespace users temporary tablespace temp;
User created.
SQL> grant create session to testuser
  2  /
Grant succeeded.
SQL> select * from pass_thief;
no rows selected
      無記錄
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
     建立一個使用者用hackprof作為自己的PFORILE
  1   create user testuser identified by "jjjjj" profile hackprof default tablespace users
  2* temporary tablespace temp
SQL> /
 create user testuser identified by "jjjjj" profile hackprof default tablespace users
*
ERROR at line 1:
ORA-28003: password verification for the specified password failed
ORA-20002: Password length less than 7
     因為密碼長度不夠而建使用者失敗,先看看記錄下使用者操作了嗎?
SQL> select id||' '||username||' '||value from pass_thief;
ID||''||USERNAME||''||VALUE
-----------------------------------------------------------------------
1 TESTUSER ***********************************
2 TESTUSER NEW PASSWORD: jjjjj
3 TESTUSER check for username=password
4 TESTUSER check for password length
 
     修改合適密碼後再建使用者
  1  create user testuser identified by "jjjjj!1" profile hackprof default tablespace users
  2*  temporary tablespace temp
SQL> /
User created.
SQL> select id||' '||username||' '||value from pass_thief;
ID||''||USERNAME||''||VALUE
--------------------------------------------------------   
19 TESTUSER ***********************************
20 TESTUSER NEW PASSWORD: jjjjj!1
21 TESTUSER check for username=password
22 TESTUSER check for password length
23 TESTUSER check for easy passwords
24 TESTUSER checking for digits in password
25 TESTUSER checking for char in password
26 TESTUSER checking for punctuation in password
27 TESTUSER checking for password != old_password
28 TESTUSER old_password is null
29 TESTUSER returned TRUE
     從pass_thief可以看到詳細的使用者操作及系統判斷過程,hackprof的密碼必須長度超過7,必須包含字元,數字,特殊字元.你還可以透過UTL_SMTP 把密碼修改的情況隨時MAIL給你或DBA
SQL> grant create session to testuser;
Grant succeeded.
SQL> connect testuser/jjjjj!1
Connected.
     使用者對密碼的修改都會詳細儲存在pass_thief表裡,但HACKER還是有兩個方法來破壞你的密碼策略。
     一,HACKER可以透過修改你的verify_function_test,這些人首先要有DBA級許可權或系統管理員以及一些有許可權對DICTIONARY有許可權的人,當HACKER這麼做之後你甚至不回察覺出你的系統已經不安全了。
     二,HACKER會透過將自己編寫的程式碼移植到系統或DBA執行的PACKAGE裡,比如修改$ORACLE_HOME/sqlplus/admin/glogin.sql 以得到密碼或修改密碼是相當容易的一件事情。
     你不能把這些可能對你的系統有惡意的人全部排除到系統之外,這些人包括內部的(非故意)和外部人員(故意),但你可以讓你的系統看起來對HACKER沒慾望,或你應該能想辦法及時掌握密碼的修改情況。
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
     對有人修改verify_function_test的問題可以透過收集FUNCTION的HASH VALUE,並比較兩次HASH VALUE的異同來判斷你的FUNCTION是否被別人修改了。(ORACLE 10G)
     set serveroutput on size 1000000
declare
   hash varchar2(32);
   code clob;
begin
   for frec in (
      select text
      from dba_source
      where wner = 'SYS'
      and type = 'FUNCTION'
      and name = 'VERIFY_FUNCTION_TEST'
      order by line
   )
   loop
      code := code || frec.text;
   end loop;
   hash := rawtohex(
      dbms_crypto.hash (
         typ => dbms_crypto.hash_md5,
         src => code
      )
   );
   dbms_output.put_line('HASH: ' || hash);
end;
/
       你還可以透過做一個DATABASE級別的TRIGGER來阻止HACKER直接修改FUNCTION
 create or replace trigger verify_trg
  before create or alter or drop on database
  begin
            if ora_dict_obj_name = 'VERIFY_FUNCTION_TEST'
                    or ora_dict_obj_type = 'PROFILE' then
                    raise_application_error(-20000,'Action not allowed on verify function');
            end if;
 end;
SQL> /
Trigger created.
    試圖刪除PROFILE
SQL>  drop profile hackprof cascade
  2  /
 drop profile hackprof cascade
*
ERROR at line 1:
ORA-00604: error occurred at recursive SQL level 1
ORA-20000: Action not allowed on verify function
ORA-06512: at line 4
四,審計SYS帳戶的操作也是一個好注意,ORACLE 提供AUDIT_SYS_OPERATIONS引數,定義如下
AUDIT_SYS_OPERATIONS enables or disables the auditing of operations issued by user SYS, and users connecting with SYSDBA or SYSOPER privileges. The audit records are written to the operating system's audit trail.
  1* alter system set audit_sys_operations=true scope =spfile
SQL> /
System altered.
SQL>
       審計連線事件
SQL> audit connect whenever unsuccessful;
SQL> audit disconnect whenever unsuccessful;
       審計會產生相當大的資料,並會影響系統效能,因此如果一定要開審計的話一定要把SYS.AUD$表放到非SYSTEM表空間。一些HACKER甚至知道對方啟用DB審計功能後就惡意攻擊以觸發審計事件而導致DB無法使用,這種做法損人不利己。
    你可以設定audit_trail為 NONE,OS或DB ,如果設定了OS那麼還要設定audit_file_dest 為具體OS路徑。

                        日月明王

本文轉自:http://sunmoonking.spaces.live.com/blog/cns!E3BD9CBED01777CA!187.entry

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/90618/viewspace-613309/,如需轉載,請註明出處,否則將追究法律責任。

相關文章