oracle 密码安全 (zt)


密码安全 
                                     日月明王
一,简介
    思考密码安全必须以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/,如需转载,请注明出处,否则将追究法律责任。

转载于:http://blog.itpub.net/90618/viewspace-613309/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值