0


ORACLE密码复杂度校验脚本utlpwdmg.sql解析

ORACLE数据库系统可以说是当今市面上最常见的数据库系统了,相信各位在工作和学习的过程中经常能接触到ORACLE数据库,那么utlpwdmg.sql作为ORACLE自带的密码复杂度校验脚本来说,对ORACLE的安全也举足轻重。可能由于我搜索姿势不对,在网上很少找到关于utlpwdmg.sql这个文件的文章(可能因为太简单了???),在下便在此浅显的分析下该文件。本人主要目的是在此记录一下自己分析和学习的过程,可能会有不对的地方,也请各位大佬多多指点。


前言

作为一个使用数据库的人,我们经常会对数据库是否安全产生顾虑,而密码复杂度和密码策略则是安全计算环境的重要组成部分。那么本文就以ORACLE11g数据库的密码复杂度校验脚本utlpwdmg.sql为例,分析该脚本中各项参数的内容,以及如何修改使其符合等保标准


一、utlpwdmg.sql是什么?

utlpwdmg.sql是ORACLE数据库自带的密码校验脚本。其路径一般为$ORACLE_HOME/RDBMS/ADMIN/utlpwdmg.sql

二、使用步骤

1.进入对应路径

cd $ORACLE_HOME/RDBMS/ADMIN/

如不进入该路径,则会在应用脚本时报错

SQL> @utlpwdmg.sql
SP2-0310: 无法打开文件 "utlpwdmg.sql"

2.登录数据库

sqlplus /nolog

3.使用sysdba应用utlpwdmg.sql

SQL>conn / as sysdba
SQL>@utlpwdmg.sql

如回显如下则说明应用成功

函数已创建。

授权成功。

配置文件已更改

函数已创建。

授权成功。

4.结果查询

SQL>  select * from dba_profiles where profile='DEFAULT';

PROFILE                        RESOURCE_NAME                    RESOURCE LIMIT
------------------------------ -------------------------------- -------- ----------------------------------------
DEFAULT                        COMPOSITE_LIMIT                  KERNEL   UNLIMITED
DEFAULT                        SESSIONS_PER_USER                KERNEL   UNLIMITED
DEFAULT                        CPU_PER_SESSION                  KERNEL   UNLIMITED
DEFAULT                        CPU_PER_CALL                     KERNEL   UNLIMITED
DEFAULT                        LOGICAL_READS_PER_SESSION        KERNEL   UNLIMITED
DEFAULT                        LOGICAL_READS_PER_CALL           KERNEL   UNLIMITED
DEFAULT                        IDLE_TIME                        KERNEL   UNLIMITED
DEFAULT                        CONNECT_TIME                     KERNEL   UNLIMITED
DEFAULT                        PRIVATE_SGA                      KERNEL   UNLIMITED
DEFAULT                        FAILED_LOGIN_ATTEMPTS            PASSWORD 10
DEFAULT                        PASSWORD_LIFE_TIME               PASSWORD 180
DEFAULT                        PASSWORD_REUSE_TIME              PASSWORD UNLIMITED
DEFAULT                        PASSWORD_REUSE_MAX               PASSWORD UNLIMITED
DEFAULT                        PASSWORD_VERIFY_FUNCTION         PASSWORD VERIFY_FUNCTION_11G
DEFAULT                        PASSWORD_LOCK_TIME               PASSWORD 1
DEFAULT                        PASSWORD_GRACE_TIME              PASSWORD 7

可以看到 PASSWORD_VERIFY_FUNCTION 字段已经被指定为了 VERIFY_FUNCTION_11G 密码复杂度校验函数。

三、utlpwdmg.sql分析

打开utlpwdmg.sql,我们可以看到在文件头部做了一些注释,说明了该文件的作用,表明

“这是一个通过设置默认密码资源限制来启用密码管理功能的脚本”,当然这里不是重点。

Rem
Rem $Header: rdbms/admin/utlpwdmg.sql /st_rdbms_11.2.0/1 2013/01/31 01:34:11 skayoor Exp $
Rem
Rem utlpwdmg.sql
Rem
Rem Copyright (c) 2006, 2013, Oracle and/or its affiliates. 
Rem All rights reserved. 
Rem
Rem    NAME
Rem      utlpwdmg.sql - script for Default Password Resource Limits
Rem
Rem    DESCRIPTION
Rem      This is a script for enabling the password management features
Rem      by setting the default password resource limits.
Rem
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      customer wants to make on the new password.
Rem
Rem    MODIFIED   (MM/DD/YY)
Rem    skayoor     01/17/13 - Backport skayoor_bug-14671375 from main
Rem    asurpur     05/30/06 - fix - 5246666 beef up password complexity check 
Rem    nireland    08/31/00 - Improve check for username=password. #1390553
Rem    nireland    06/28/00 - Fix null old password test. #1341892
Rem    asurpur     04/17/97 - Fix for bug479763
Rem    asurpur     12/12/96 - Changing the name of password_verify_function
Rem    asurpur     05/30/96 - New script for default password management
Rem    asurpur     05/30/96 - Created
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/<password> as sysdba before running the script

接着,文件中定义了一个密码复杂度校验函数 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;
   db_name varchar2(40);
   digitarray varchar2(20);
   chararray varchar2(52);
   i_char varchar2(10);
   simple_password varchar2(10);
   reverse_user varchar2(32);

接着是函数体部分,这块儿才是重点 ,BEGIN开始函数体,为变量digitarray、chararray赋值,用于后续检测密码中是否含有数字和大小写字母。

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

接着正式进入了密码复杂度检测的部分,首先对密码长度进行了检测,要求密码长度大于等于8位,若密码长度小于8位,则放回一个错误-20001

   -- 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的数字相同,则返回错误 -20002,-20005

   -- 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;

判断密码与用户名的逆序是否相同,若相同则返回错误 -20003

   -- 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;

比对了密码和服务器名的相似度,若密码与服务器名相同或与服务器名后拼接上1-100的数字相同,则返回错误 -20004,-20005

   -- 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;

比对密码是否为‘welcome1’、‘database1’、‘account1’、‘user1234’这类弱口令,若是则返回错误-20006

   -- 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;

比对密码是否为oracle或oracle拼接上1-100的数字,若相同则返回错误 -20007

   -- 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;

接下来是verify_function_11G函数判断密码是否含有数字和字符的部分

判断密码是否包含至少一个数字(0-9),若不包含则返回错误-20008

 -- 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 should contain at least one \
              digit, one character');
   END IF;

判断密码是否包含至少一个字母(a-z,A-Z),若不包含则返回错误-20009

   -- 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 should contain at least one \
              digit, one character');
   END IF;
   <<endsearch>>

到这一步,VERIFY_FUNCTION_11G这个密码复杂度校验函数就结束了。

判断新密码是否与原密码至少存在3个不同的字符,如果不存在则返回错误-20011

   -- 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;
/

赋予PUBLIC用户verify_function的函数执行权限,并更改ORACLE的profile文件,配置密码的最常使用期限为180天、密码过期锁定时间为7天(即密码超过180天使用期限后若7天内不更改密码则会将该账户锁定)、密码重用时间为不限制、密码重用最大时间为不限制(两个参数都为UNLIMITED时,密码可以随意重用,两参数均为指定值时,必须都满足才可以重用密码。两参数有其中一个不为UNLIMITED,则密码不能重用)、登陆失败锁定次数为10次、密码锁定时间为一天、密码复杂度校验函数为verify_function_11G。

GRANT EXECUTE ON verify_function_11G TO PUBLIC;

-- 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;

以上就是 utlpwdmg.sql文件里面的一个标准组成模板,后续内容与其大同小异,在此不做赘述。

可以看到verify_function_11G该函数仅对密码复杂度做了最简单的判断和过滤,完全不能符合当前情况下的信息安全基线要求。文件中已经给出了大体的校验框架,我们仅仅需要对密码复杂度校验函数verify_function_11G做一些小小的修改就可以使其符合当前情况下的信息安全基线要求。

四、修改密码复杂度校验函数verify_function_11G

为了使得密码符合要求:至少包含大写字母、小写字母、数字、特殊字符 四种其中的三种,首先我们应修改变量chararray varchar2(52),将其分割成upperchar varchar2(26)和lowerchar varchar2(26),并新声明变量punctarray varchar2(25)、flagfortypes integer

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;
   db_name varchar2(40);
   digitarray varchar2(20);
   --chararray varchar2(56);
   punctarray varchar2(25);
   upperchar varchar2(26);
   lowerchar varchar2(26);
   flagfortypes  integer;
   i_char varchar2(10);
   simple_password varchar2(10);
   reverse_user varchar2(32);

分别为四类变量赋值 digitarray punctarray upperchar lowerchar和标志 flagfortypes

BEGIN 
   digitarray:= '0123456789';
   upperchar:= 'abcdefghijklmnopqrstuvwxyz';
   lowerchar:= 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
   punctarray:='!"#$%&()``*+,-/:;<=>?_'; 
   flagfortypes:=0

接下来,我们需要对密码复杂度函数verify_function_11G的检查是否含有数字和字符部分进行修改

   -- Check if the password contains 3 types of the listed array:integer,upperchar,lowerchar,punct

   -- 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;
            flag=flag+1;
            GOTO findupper;
         END IF;
      END LOOP;
   END LOOP;

   -- 2. Check for the uppercharacter
   <<findupper>>
   ischar:=FALSE;
   FOR i IN 1..length(upperchar) LOOP 
      FOR j IN 1..m LOOP 
         IF substr(password,j,1) = substr(upperchar,i,1) THEN
            ischar:=TRUE;
            flag=flag+1;
            GOTO findlower;
         END IF;
      END LOOP;
   END LOOP;

   -- 3. Check for the lowercharacter
   <<findlower>>
   ischar:=FALSE;
   FOR i IN 1..length(lowerchar) LOOP 
      FOR j IN 1..m LOOP 
         IF substr(password,j,1) = substr(lowerchar,i,1) THEN
            ischar:=TRUE;
            flag=flag+1;
            GOTO findpunct;
         END IF;
      END LOOP;
   END LOOP;

   -- 4. Check for the punctarray
   <<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;
            flag=flag+1;
            GOTO checktypes;
         END IF;
      END LOOP;
   END LOOP;

   -- 5. Check for the types
   <<checktypes>>
   IF flag <3 THEN
              raise_application_error(-20008, 'Password should contain at least 3 types of the listed array:integer,upperchar,lowerchar,punct');
   END IF;

这样,VERIFY_FUNCTION_11G这个密码复杂度校验函数就会对密码进行字符类别数量检测,如果字符数量小于3,则会报错。接下来我们还需要修改profile文件的配置将登陆失败锁定次数改为5次、密码锁定时间为10分钟。

GRANT EXECUTE ON verify_function_11G TO PUBLIC;

-- 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 5
PASSWORD_LOCK_TIME 1/144
PASSWORD_VERIFY_FUNCTION verify_function_11G;

到此,已经可以初步符合当前情况下的信息安全基线要求。

需要再次重复第二节中的内容对utlpwdmg.sql进行应用,以上的修改才会生效


总结

以上就是今天要讲的内容,本文仅仅简单介绍了oracle的utlpwdmg.sql组件的使用和修改过程,而oracle还提供了大量的脚本和组件,欢迎各位一起补充

标签: oracle 安全

本文转载自: https://blog.csdn.net/weixin_43900811/article/details/121509730
版权归原作者 weixin_43900811 所有, 如有侵权,请联系我们删除。

“ORACLE密码复杂度校验脚本utlpwdmg.sql解析”的评论:

还没有评论