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