20

等保测评2.0:Oracle身份鉴别(中)

 4 years ago
source link: https://www.freebuf.com/articles/database/233009.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

一、说明

本篇文章主要说一说oracle数据库中身份鉴别控制点中测评项a的相关内容和理解,关于身份鉴别的基础知识点在《 等保测评2.0:Oracle身份鉴别(上) 》中,请大家先阅读它。

二、测评项a

a)应对登录的用户进行身份标识和鉴别,身份标识具有唯一性,身份鉴别信息具有复杂度要求并定期更换;

三、测评项a要求1

应对登录的用户进行身份标识和鉴别

oracle使用用户名对登录用户进行身份标识,这没啥好说的。oracle在身份鉴别这一块,拥有密码文件验证和操作系统验证两种方式。

密码文件验证的话,是需要登录用户输入用户名、口令的,但是操作系统验证则不需要。

对于数据库特殊账户的操作系统验证,仅由sqlnet.ora文件中的SQLNET.AUTHENTICATION_SERVICES进行控制,至于它有什么值以及代表什么意义,请大家去看上篇。

这里大家如果不想麻烦的话,其实也没有必要把SQLNET.AUTHENTICATION_SERVICES参数的值在linux和windows系统中代表的意义都搞清楚,测评的时候直接测试即可。

以centos系统为例子,也就是测评的时候先登录oracle数据库服务器的oracle账户,如果被测评方不清楚oracle账户的口令,也可以登录到root账户,然后使用“su – oracle”命令切换过去。

其实这里需要登录的是属于oinstall用户组的用户,但是一般情况下没人会去改默认配置,默认安装情况下操作系统的oracle用户会属于oinstall用户组。

登录oracle账户成功后,使用sqlplus / as sysdba命令去登录oracle数据库,这里就是使用数据库特殊账户的操作系统验证方式去验证。

如果能登录成功则说明开启了这种验证方式,不能则说明没开启。

如果开启了,那么这个要求项它就不能满足了,顶多是部分符合,因为这种方式登录数据库特殊账户不需要用户名、密码(虽然只能在本地登录,且登录的操作系统账户需要隶属于oinstall用户组),是一个比较大的安全隐患。

还有一种针对数据库普通账户的操作系统验证方式,这种验证方式对于登录的操作系统账户没有啥要求。

先去查看remote_os_authent和os_authent_prefix参数,它们的默认值分别是FALSE和ops$。

如果remote_os_authent是FALSE,那么是否开启这种验证方式还是由SQLNET.AUTHENTICATION_SERVICES决定。

如果开启了,假定os_authent_prefix的值是ops$,假定操作系统中存在一个cv的账户。

那么只要oracle数据库中建立一个名为ops$cv的账户并给与基础权限(连接权限等),用户在登录操作系统中的cv后,可以直接使用sqlplus /或sqlplus / as normal(两个命令是一个意思),以普通账户的身份登录ops$cv账户(当然这里需在cv账户的环境变量里配置一下好方便的使用sqlplus命令)。

同样的,如果开启了这种验证方式,那么也就顶多是部分符合了。

如果remote_os_authent为TRUE,那不用看了,数据库普通账户的操作系统验证方式肯定开启了,并且数据库普通账户的远程操作系统验证方式也开启了。

还是用上面的数据举例子,假定os_authent_prefix的值是ops$,假定远程终端存在一个cv的操作系统账户,那么在远程终端那也可以用空用户名、空口令的方式登录ops$cv账户,更不安全了。

至于空口令,在oracle中似乎不能设置空口令,我反正没有尝试成功……

四、测评项a要求2

身份标识具有唯一性

即用户名不会重复,oracle自动实现,默认符合。

五、测评项a要求3

身份鉴别信息具有复杂度要求

这个要从两个方面看,我个人觉得两个方面都符合才能算达到要求。

第一个方面即实际的口令是否具有一定的复杂度,也即口令至少8位,且包含大写字母、小写字母、数字、特殊字符这四类字符种的三种,且口令不包含简单排列规律,如admin!@#123此类弱口令。

第二个方面就是在oracle中是否设置了口令复杂度策略,这里要看的就是配置表中的PASSWORD_VERIFY_FUNCTION字段的值:

select * from dba_profiles;

BZruArU.jpg!web

这里插一句嘴,说一说配置表的规则,用户是可以自定义多个配置给不同的用户使用。

比如这里的PASSWORD_VERIFY_FUNCTION的Profile字段值为Default,某用户要使用Default的相关配置,就要设置DBA_USERS表中的Profile字段值为Default。

默认情况下,用户使用的都是Default:

select username,profile from DBA_USERS

ZZvAjaF.jpg!web

好,咱们说一说PASSWORD_VERIFY_FUNCTION字段是什么意思,该字段的值应该为oracle中某函数对象的名字,当创建、更改用户口令时会调用到该函数对口令进行校验,默认情况下这里的值是null,也即不使用任何函数对口令进行校验。

在初级教材中,让我们去查看utlpwdmg.sql中的相关信息,其实是不准确的。

因为utlpwdmg.sql并不是函数本身,它只是创建函数的一段语句而已。

实际上在oracle11g中,运行utlpwdmg.sql会创建两个函数,一个是新版本的口令校验函数,一个是老版本的,在里面还会对配置表进行修改,让PASSWORD_VERIFY_FUNCTION的值为新版本的口令校验函数的名字。

这个是utlpwdmg.sql文件中新版本的口令校验函数FUNCTION verify_function_11G的定义语句:

CREATE OR REPLACE FUNCTION verify_function_11G
(username varchar2,
  password varchar2,
  old_password varchar2)
  RETURN boolean IS 
   n boolean;
   m integer;
   differ integer;
   isdigit boolean;
   ischar  boolean;
   ispunct boolean;
   db_name varchar2(40);
   digitarray varchar2(20);
   punctarray varchar2(25);
   chararray varchar2(52);
   i_char varchar2(10);
   simple_password varchar2(10);
   reverse_user varchar2(32);

BEGIN 
   digitarray:= '0123456789';
   chararray:= 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';

   -- Check for the minimum length of the password
   IF length(password) < 8 THEN
      raise_application_error(-20001, 'Password length less than 8');
   END IF;


   -- Check if the password is same as the username or username(1-100)
   IF NLS_LOWER(password) = NLS_LOWER(username) THEN
     raise_application_error(-20002, 'Password same as or similar to user');
   END IF;
   FOR i IN 1..100 LOOP
      i_char := to_char(i);
      if NLS_LOWER(username)|| i_char = NLS_LOWER(password) THEN
        raise_application_error(-20005, 'Password same as or similar to user name ');
      END IF;
    END LOOP;

   -- Check if the password is same as the username reversed
   
   FOR i in REVERSE 1..length(username) LOOP
     reverse_user := reverse_user || substr(username, i, 1);
   END LOOP;
   IF NLS_LOWER(password) = NLS_LOWER(reverse_user) THEN
     raise_application_error(-20003, 'Password same as username reversed');
   END IF;

   -- Check if the password is the same as server name and or servername(1-100)
   select name into db_name from sys.v$database;
   if NLS_LOWER(db_name) = NLS_LOWER(password) THEN
      raise_application_error(-20004, 'Password same as or similar to server name');
   END IF;
   FOR i IN 1..100 LOOP
      i_char := to_char(i);
      if NLS_LOWER(db_name)|| i_char = NLS_LOWER(password) THEN
        raise_application_error(-20005, 'Password same as or similar to server name ');
      END IF;
    END LOOP;

   -- 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.
   IF NLS_LOWER(password) IN ('welcome1', 'database1', 'account1', 'user1234', 'password1', 'oracle123', 'computer1', 'abcdefg1', 'change_on_install') THEN
      raise_application_error(-20006, 'Password too simple');
   END IF;

   -- Check if the password is the same as oracle (1-100)
    simple_password := 'oracle';
    FOR i IN 1..100 LOOP
      i_char := to_char(i);
      if simple_password || i_char = NLS_LOWER(password) THEN
        raise_application_error(-20007, 'Password too simple ');
      END IF;
    END LOOP;

   -- Check if the password contains at least one letter, one digit 
   -- 1. Check for the digit
   isdigit:=FALSE;
   m := length(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(-20008, 'Password must contain at least one digit, one character');
   END IF;
   -- 2. Check for the character
   <<findchar>>
   ischar:=FALSE;
   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 endsearch;
         END IF;
      END LOOP;
   END LOOP;
   IF ischar = FALSE THEN
      raise_application_error(-20009, 'Password must contain at least one \
              digit, and one character');
   END IF;


   <<endsearch>>
   -- Check if the password differs from the previous password by at least
   -- 3 letters
   IF old_password IS NOT NULL THEN
     differ := length(old_password) - length(password);

     differ := abs(differ);
     IF differ < 3 THEN
       IF length(password) < length(old_password) THEN
         m := length(password);
       ELSE
         m := length(old_password);
       END IF;

       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(-20011, 'Password should differ from the \
            old password by at least 3 characters');
       END IF;
     END IF;
   END IF;
   -- Everything is fine; return TRUE ;   
   RETURN(TRUE);
END;
/

然后在这里,对配置文件进行了修改,设置PASSWORD_VERIFY_FUNCTION的值为verify_function_11G:

-- This script alters the default parameters for Password Management
-- This means that all the users on the system have Password Management
-- enabled and set to the following values unless another profile is 
-- created with parameter values set to different value or UNLIMITED 
-- is created and assigned to the user.

ALTER PROFILE DEFAULT LIMIT
PASSWORD_LIFE_TIME 180
PASSWORD_GRACE_TIME 7
PASSWORD_REUSE_TIME UNLIMITED
PASSWORD_REUSE_MAX UNLIMITED
FAILED_LOGIN_ATTEMPTS 10
PASSWORD_LOCK_TIME 1
PASSWORD_VERIFY_FUNCTION verify_function_11G;

这个是老版本的校验函数 FUNCTION verify_function 的定义语句:

-- Below is the older version of the script

-- 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/<password> as sysdba before running the script

CREATE OR REPLACE FUNCTION verify_function
(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);

BEGIN 
   digitarray:= '0123456789';
   chararray:= 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
   punctarray:='!"#$%&()``*+,-/:;<=>?_';

   -- Check if the password is same as the username
   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
   IF length(password) < 4 THEN
      raise_application_error(-20002, 'Password length less than 4');
   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.
   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);
   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
   <<findchar>>
   ischar:=FALSE;
   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
   <<findpunct>>
   ispunct:=FALSE;
   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;

   <<endsearch>>
   -- Check if the password differs from the previous password by at least
   -- 3 letters
   IF old_password IS NOT NULL THEN
     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;
   END IF;
   -- Everything is fine; return TRUE ;   
   RETURN(TRUE);
END;
/

所以,实际上想要知道现在实际使用的校验函数的内容,就应该直接在数据库中查看校验函数的内容,而不是去查看utlpwdmg.sql文件。

在这里,使用的是verify_function_11G,由于其内容我没有修改过,所以和utlpwdmg.sql中所定义的是一样的:

2IfmMzY.jpg!web

至于其中的内容嘛,注释写得很清楚的,我就从上到下大概说明下:

检查口令的最小长度,如果小于8就返回错误,所以口令的最小长度是9

-- Check for the minimum length of the password
   IF length(password) < 8 THEN
      raise_application_error(-20001, 'Password length less than 8');
   END IF;

检查被创建或被更改口令的账户的用户名是否和新的口令一样,如果一样返回错误

检查新的口令是是否等于用户名后面加上1到100的数字,比如用户名时user,那么口令如果是user0到user100中的任何一个,也会返回错误。

-- Check if the password is same as the username or username(1-100)
   IF NLS_LOWER(password) = NLS_LOWER(username) THEN
     raise_application_error(-20002, 'Password same as or similar to user');
   END IF;
   FOR i IN 1..100 LOOP
      i_char := to_char(i);
      if NLS_LOWER(username)|| i_char = NLS_LOWER(password) THEN
        raise_application_error(-20005, 'Password same as or similar to user name ');
      END IF;
    END LOOP;

这里是检查口令是否等于倒序的用户名,或者与其相似,比如用户名是user,则口令不能是r、re、res、resu中的任何一个

-- Check if the password is same as the username reversed
   
   FOR i in REVERSE 1..length(username) LOOP
     reverse_user := reverse_user || substr(username, i, 1);
   END LOOP;
   IF NLS_LOWER(password) = NLS_LOWER(reverse_user) THEN
     raise_application_error(-20003, 'Password same as username reversed');
   END IF;

这里检查口令是否和数据库名相等,以及是否和数据库名0到数据库名100相等。

我这里是默认的数据库名:ORCL

RBnMfuR.jpg!web

-- Check if the password is the same as server name and or servername(1-100)
   select name into db_name from sys.v$database;
   if NLS_LOWER(db_name) = NLS_LOWER(password) THEN
      raise_application_error(-20004, 'Password same as or similar to server name');
   END IF;
   FOR i IN 1..100 LOOP
      i_char := to_char(i);
      if NLS_LOWER(db_name)|| i_char = NLS_LOWER(password) THEN
        raise_application_error(-20005, 'Password same as or similar to server name ');
      END IF;
    END LOOP;

检查口令是否是一些简单的单词,只不过这里只是一个示例而已,用户可以根据需要多添加一些弱口令。

-- 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.
   IF NLS_LOWER(password) IN ('welcome1', 'database1', 'account1', 'user1234', 'password1', 'oracle123', 'computer1', 'abcdefg1', 'change_on_install') THEN
      raise_application_error(-20006, 'Password too simple');
   END IF;

检查口令是否是oracle1到oracle100中的一个,不过这里口令可以等于oracle,不知道为啥这里是否等于oracle的校验

-- Check if the password is the same as oracle (1-100)
    simple_password := 'oracle';
    FOR i IN 1..100 LOOP
      i_char := to_char(i);
      if simple_password || i_char = NLS_LOWER(password) THEN
        raise_application_error(-20007, 'Password too simple ');
      END IF;
    END LOOP;

检查口令是否包含至少一个数字和一个字母(同时包含才可以通过校验)

由于这里字母数组的定义是:chararray:= ‘abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ’;

所以指的是至少一个大写或小写字母。

-- Check if the password contains at least one letter, one digit 
   -- 1. Check for the digit
   isdigit:=FALSE;
   m := length(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(-20008, 'Password must contain at least one digit, one character');
   END IF;
   -- 2. Check for the character
   <<findchar>>
   ischar:=FALSE;
   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 endsearch;
         END IF;
      END LOOP;
   END LOOP;
   IF ischar = FALSE THEN
      raise_application_error(-20009, 'Password must contain at least one \
              digit, and one character');
   END IF;

检查口令和旧口令的相似程度:

假设新口令是user,旧口令是admin

新口令长度和旧口令长度的差值大于等于3的话,直接通过校验。

这里的差值是1,所以需要进行校验。

循环的次数为长度小的那一边的口令的长度,也就是user字符串的长度4。

顺序取字符串来对比:

u不等于a,differ的值加1,为1
s不等于d,differ加1,为2

最后循环4次结束,differ为4,

如果最后differ小于3,则未通过校验,大于等3,则通过校验。

-- Check if the password differs from the previous password by at least
   -- 3 letters
   IF old_password IS NOT NULL THEN
     differ := length(old_password) - length(password);

     differ := abs(differ);
     IF differ < 3 THEN
       IF length(password) < length(old_password) THEN
         m := length(password);
       ELSE
         m := length(old_password);
       END IF;

       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(-20011, 'Password should differ from the \
            old password by at least 3 characters');
       END IF;
     END IF;
   END IF;

最后,如果通过了所有的校验(中途没有返回错误),就直接返回true:

-- Everything is fine; return TRUE ;   
   RETURN(TRUE);

从整个默认的新版的校验函数看来,主要就是口令长度不低于9位,需要同时包含数字、字母(大小写皆可),感觉稍微差了那么一点。

理论上比较好的应该是口令长度不低于8位(这里是9位当然更好了),同时包含数字、大写字母、小写字母、特殊字符其中的3种字符。

另外,不知道为什么,SYS账户的口令的更改,不受到这个校验函数的限制。

登录SYS按道理来说使用的是密码文件或操作系统的验证,所以不知道在USER$中SYS账户的PASSWORD字段的值有什么用,虽然也会跟着口令的更改而更改就是了。

六、测评项a要求4

并定期更换

一方面查看实际的更换时间:

select name,PTIME from USER$

yiuIruz.jpg!web

另一方面查看相关的策略,先看和口令过期直接相关的两个参数:

PASSWORD_LIFE_TIME和PASSWORD_GRACE_TIME

Irq6Zr7.jpg!web

这里的时间单位是天,我这里自己修改过,默认情况下PASSWORD_LIFE_TIME的值是180,PASSWORD_GRACE_TIME的值是7。

PASSWORD_LIFE_TIME代表口令过期时间,而PASSWORD_GRACE_TIME则代表口令过期后的宽恕时间。

口令过期但还未超过宽恕期时,登录时会提醒你口令在多久后过期(这里的过期是指超过宽恕期),询问你是否修改口令,可以不修改,然后正常登录账户。

但是超过宽恕期再登录,就无法登录上去了。

oracle在这里的逻辑是这样的,需要通过用户的登录来改变用户的状态。

举个例子,假如PASSWORD_LIFE_TIME的值是1,PASSWORD_GRACE_TIME的值是也是1。

我在2020年4月7日10点整,创建了一个账户,在DBA_USERS表中,该账户的CREATED(创建时间)就是2020年4月7日10点,ACCOUNT_STATUS(账户状态)是OPEN,在USER$表中该账户的PTIME(口令修改时间)也是2020年4月7日10点整。

在DBA_USERS表中,该账户的EXPIRY_DATE是PTIME+PASSWORD_LIFE_TIME的值,也就是2020年4月8日10点整。

(注意,在USER$表中也有一个EXPTIME字段,但是好像这个字段有时候不是实时的,需要用户的登录才会更新,比如现在,该字段就是一个空字符串)

那么,在到达2020年4月8日10点整后,这个账户的ACCOUNT_STATUS是不会自动变成EXPIRED(GRACE)(口令过期但还处于宽恕期内),只要你不登录这个账户,那么该账户会一直处于OPEN状态。哪怕是过了两天,也就是超过PASSWORD_LIFE_TIME+PASSWORD_GRACE_TIME的值,仍然是OPEN状态。

在2020年4月8日10点整后,登录该账户,会更新账户的ACCOUNT_STATUS,更新成EXPIRED(GRACE),而EXPIRY_DATE也会更新,更新为登录时间点+PASSWORD_GRACE_TIME的值。

同样的,如果在这之后一直不登录账户,则超过宽恕期后,账户的状态还是EXPIRED(GRACE),只有等你尝试登录这个账户时,才会更新状态值为EXPIRED,当然这个时候你也登录不上了。

所以根据账户状态值不同,EXPIRY_DATE代表的意思有所不同,状态值为OPEN,则代表过期时间是什么时间。状态值为EXPIRED(GRACE),则代表宽恕期结束是什么时间。

另外,更新PASSWORD_LIFE_TIME值,也会实时的更新账户的EXPIRY_DATE值,但是仅对处于OPEN状态的账户有效。

如某账户为OPEN状态,其EXPIRY_DATE值为2020年4月8日10点,此时将PASSWORD_LIFE_TIME更改为2,则EXPIRY_DATE值就变成2020年4月9日10点了。

但是如果该账户是处于EXPIRED(GRACE)状态,修改PASSWORD_LIFE_TIME或PASSWORD_GRACE_TIME对它都是没用的。

不知道为什么,SYS账户不受到口令过期的限制,我测试的时候确实是这样。

可能是因为它属于特殊账户?毕竟判断是否过期也要去先读取数据库,而SYS账户可以在数据库未开启的时候连接它。

与口令过期间接相关的两个参数:PASSWORD_REUSE_TIME和PASSWORD_REUSE_MAX

qmMRfeF.jpg!web

PASSWORD_REUSE_TIME:指定了口令在多少天内不能重用,单位为1天。
PASSWORD_REUSE_MAX:指定了当前口令在被重用之前需要更改几次。

这里的重用是针对每个账户的自己的口令,如果不设置这两个参数,设置账户新口令的时候就可以使用当前的口令,那么口令过期就不存在意义了。

这里光看说明是看是不太容易理解的,试一试就明白了:

在PASSWORD_REUSE_TIME、PASSWORD_REUSE_MAX均为默认值UNLIMITED的时候,修改口令的时候可以随意重用,用当前的口令当新口令也都可以。

这里会突然想到verify_function_11G,如果新口令和旧口令一样的话,应该通不过校验的才对。

经过实验,不知道是为什么old_password是空字符串,压根就没有传值进来,所以实际上直接跳过了旧口令和新口令的对比校验……

当PASSWORD_REUSE_TIME、PASSWORD_REUSE_MAX均非默认值UNLIMITED的时候,则需要两个条件都满足才可以更改口令。

如PASSWORD_REUSE_TIME从UNLIMITED变成1,以及PASSWORD_REUSE_MAX从UNLIMITED变成1开始,oracle开始记录使用过的口令。

如某账户,这个时候依次设置新口令a、b、c、d、e,此时的口令是e,在1天内,该账户设置新口令的话,使用a、b、c、d、e中的任何一个都不可以。

超过1天后,a、b、c、d均可作为新口令,但是e不可以,因为PASSWORD_REUSE_MAX的值是1,相当于从e往前数,有1个口令不能被当做新口令,那就是e自己了。如果PASSWORD_REUSE_MAX的值是2,那么e、d均不可以作为新口令。

而如果PASSWORD_REUSE_TIME为unlimited、PASSWORD_REUSE_MAX为某个数值,或者PASSWORD_REUSE_TIME为某个数值,PASSWORD_REUSE_MAX为unlimited的时候,账户使用的口令都不能当做新口令进行设置。

所以为了满足定期更换的要求,PASSWORD_REUSE_TIME、PASSWORD_REUSE_MAX都应该设置为非unlimited的值。

最后,不知道为什么,SYS账户也不受到这两个参数的限制,可以随意重用口令。

*本文作者:起于凡而非于凡,转载请注明来自FreeBuf.COM


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK