开发者

Oracle数据库密码复杂度校验脚本utlpwdmg.sql详细解析

目录
  • 前言
  • 一、utlpwdmg.sql是什么?
  • 二、使用步骤
    • 1.进入对应路径
    • 2.登录数据库
    • 3.使用sysdba应用utlpwdmg.sql
    • 4.结果查询 
  • 三、utlpwdmg.sql分析
    • 四、修改密码复杂度校验函数verify_function_11G
      • 总结

        前言

        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 www.devze.com                       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&nbshttp://www.devze.comp;密码复杂度校验函数。

        三、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
              rais编程e_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);
           reveandroidrse_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的文章就介绍到这了,更多相关密码复杂度校验脚本utlpwdmg.sql内容请搜索编程客栈(www.devze.com)以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程客栈(www.devze.com)!

        0

        上一篇:

        下一篇:

        精彩评论

        暂无评论...
        验证码 换一张
        取 消

        最新数据库

        数据库排行榜