비밀번호 관리

    이번에는 이 장 전반부에서 학습했던 데이터 암호화와 DBMS_CRYPTO 패키지의 MAC 함수를 사용해, 앞에서도 언급했던 사용자 비밀번호를 관리할 수 있는 프로시저를 만들어 보자. DBMS_CRYPTO.MAC 함수는 단방향 암호화 해시 함수로 기존의 MD5 보다 더 안전하며 키를 사용한다.

    비밀번호 관리를 위해서는 비밀번호를 생성과 비밀번호를 확인하는 두 개의 프로그램이 필요한데 여기서는 모두 함수로 작성할 것이다. 한 가지 염두에 둘 점은 MAC 함수를 사용하려면 키를 사용해야 하고 이로 인해 키를 관리할 필요가 있는데, 이는 [현장의 노하우]에서 언급했던 ch19_wrap_pkg.pv_key_string 변수를 사용할 것이다. 먼저 비밀번호를 생성하는 fn_create_pass 함수를 만들어 보자.

    입력

        CREATE OR REPLACE PACKAGE BODY my_util_pkg IS
        ...
        ...
    
        -- 비밀번호 생성
          FUNCTION fn_create_pass ( ps_input IN VARCHAR2,
                                    ps_add   IN VARCHAR2 )
                   RETURN RAW
          IS
            v_raw     RAW(32747);
            v_key_raw RAW(32747);
            v_input_string VARCHAR2(100);
          BEGIN
            -- 키 값을 가진 ch19_wrap_pkg 패키지의 pv_key_string 상수를 가져와 RAW 타입으로 변환한다.
            v_key_raw := UTL_RAW.CAST_TO_RAW(ch19_wrap_pkg.pv_key_string );
    
            -- 좀 더 보안을 강화하기 위해 두 개의 입력 매개변수와 특수문자인 $%를 조합해
            -- MAC 함수의 첫 번째 매개변수로 넘긴다.
            v_input_string := ps_input || '$%' || ps_add;
    
            -- MAC 함수를 사용해 입력 문자열을 RAW 타입으로 변환한다.
            v_raw := DBMS_CRYPTO.MAC (src => UTL_RAW.CAST_TO_RAW(v_input_string)
                                     ,typ => DBMS_CRYPTO.HMAC_SH1
                                     ,key => v_key_raw);
    
            RETURN v_raw;
          END fn_create_pass;
    

    결과

        PACKAGE BODY MY_UTIL_PKG이(가) 컴파일되었습니다.
    

    두 번째로 비밀번호를 확인하는 함수인 fn_check_pass를 만들어 보자.

    입력

        -- 비밀번호 체크
          FUNCTION fn_check_pass ( ps_input IN VARCHAR2,
                                   ps_add   IN VARCHAR2,
                                   p_raw    IN RAW )
                   RETURN VARCHAR2
          IS
            v_raw     RAW(32747);
            v_key_raw RAW(32747);
            v_input_string VARCHAR2(100);
    
            v_rtn VARCHAR2(10) := 'N';
          BEGIN
            -- 키 값을 가진 ch19_wrap_pkg 패키지의 pv_key_string 상수를 가져와 RAW 타입으로 변환한다.
            v_key_raw := UTL_RAW.CAST_TO_RAW(ch19_wrap_pkg.pv_key_string );
    
            -- 좀 더 보안을 강화하기 위해 두 개의 입력 매개변수와 특수문자인 $%를 조합해
            -- MAC 함수의 첫 번째 매개변수로 넘긴다.
            v_input_string := ps_input || '$%' || ps_add;
    
            -- MAC 함수를 사용해 입력 문자열을 RAW 타입으로 변환한다.
            v_raw := DBMS_CRYPTO.MAC (src => UTL_RAW.CAST_TO_RAW(v_input_string)
                                     ,typ => DBMS_CRYPTO.HMAC_SH1
                                     ,key => v_key_raw);
    
            IF v_raw = p_raw THEN
               v_rtn := 'Y';
            ELSE
               v_rtn := 'N';
            END IF;
    
            RETURN v_rtn;
          END fn_check_pass;
    

    결과

        PACKAGE BODY MY_UTIL_PKG이(가) 컴파일되었습니다.
    

    성공적으로 만들었으니 테스트해 볼 차례다. 먼저 다음과 같이 비밀번호를 담을 테이블을 만들어 보자.

    입력

        CREATE TABLE ch19_user ( user_id   VARCHAR2(50),   -- 사용자아이디
                                 user_name VARCHAR2(100),  -- 사용자명
                                 pass      RAW(2000));     -- 비밀번호
    

    결과

        table CH19_USER이(가) 생성되었습니다.
    

    비밀번호가 들어갈 pass 컬럼을 RAW 타입으로 만들었다는 점에 유의하자. ‘홍길동’이란 사용자를 입력해 보자.

    입력

        INSERT INTOch19_user(user_id, user_name)
        VALUES ( 'gdhong', '홍길동');
    

    결과

        1개 행 이(가) 삽입되었습니다.
        커밋되었습니다.
    

    이제 홍길동이라는 사용자가 비밀번호를 입력했을 때, 이를 MAC 함수를 통해 RAW 타입으로 변환한 뒤 ch19_user 테이블에 저장해 보자.

    입력

        DECLARE
          vs_pass VARCHAR2(20);
        BEGIN
          -- 홍길동이라는 사람이 패스워드를 HONG 이라고 입력했다고 가정한다.
          vs_pass := 'HONG';
    
          -- ch19_user 테이블에서 홍길동을 찾아내 입력된 패스워드와 이 사용자의 아이디를
          -- fn_create_pass 매개변수로 넘겨 결과값을 받아 pass 컬럼에 저장한다.
          UPDATE ch19_user
             SET pass = my_util_pkg.fn_create_pass (vs_pass , user_id)
          WHERE user_id = 'gdhong';
    

    결과

        익명 블록이 완료되었습니다.
    

    ch19_user 테이블을 조회해 보자.

    입력

        SELECT *
        FROM ch19_user;
    

    결과

    홍길동은 앞으로 계속 로그인을 할 텐데, 이때 아이디와 비밀번호가 맞는지 확인을 해야 한다. 이는 fn_check_pass 함수가 담당한다.

    입력

        DECLARE
          vs_pass VARCHAR2(20);
          v_raw raw(32747);
    
        BEGIN
          -- 홍길동이라는 사람이 패스워드를 HONG 이라고 입력했다고 가정한다.
          vs_pass := 'HONG';
          -- 테이블에서 홍길동의 패스워드를 가져와 v_raw 변수에 담는다.
          SELECT pass
            INTO v_raw
            FROM ch19_user
           WHERE user_id = 'gdhong';
    
          -- 입력한 패스워드와 아이디를 통해 비밀번호를 체크한다.
          IF my_util_pkg.fn_check_pass(vs_pass, 'gdhong', v_raw) = 'Y' THEN
             DBMS_OUTPUT.PUT_LINE('아이디와 비밀번호가 맞아요');
          ELSE
             DBMS_OUTPUT.PUT_LINE('아이디와 비밀번호가 달라요');
          END IF;
        END ;
    

    결과

        아이디와 비밀번호가 맞아요
    

    제대로 동작하는 것을 확인할 수 있다. 여기에서는 익명 블록 형태로 테스트를 진행했지만 보통은 사용자 비밀번호 생성, 로그인 아이디와 비밀번호 체크, 비밀번호 변경 등의 기능을 별도의 프로그램으로 만들어 사용하는데 fn_create_passfn_check_pass 함수만으로도 이들을 충분히 구현할 수 있을 것이다.

    MAC 함수는 단방향 암호화 해시 함수이므로 시스템 관리자라 할지라도 해당 사용자의 비밀번호를 알아내기가 매우 어렵다. 이런 특성은 사용자로 하여금 시스템 보안에 대해 더욱 신뢰를 줄 수 있을 것이다.

    신간 소식 구독하기
    뉴스레터에 가입하시고 이메일로 신간 소식을 받아 보세요.