더북(TheBook)

비밀번호 관리

이번에는 이 장 전반부에서 학습했던 데이터 암호화와 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 함수는 단방향 암호화 해시 함수이므로 시스템 관리자라 할지라도 해당 사용자의 비밀번호를 알아내기가 매우 어렵다. 이런 특성은 사용자로 하여금 시스템 보안에 대해 더욱 신뢰를 줄 수 있을 것이다.

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