데이터 암호화 방법
단방향 암호화 |
- MD5, SHA1 같은 방법으로 암호화(Encrypt)후 원래대로 복호화(Decrypt)가 필요없는 경우 - EX) 패스워드, 주민번호(복호화 불필요시) 등 |
쌍방향 암호화 | - DES, DES3, ENC, COMPRESS 같은 방법으로 암호화(Encrypt)후 원래대로 복호화(Decrypt)가 필요한 경우 - EX) 이름, 아이디, 주민번호 (나이계산, 생일), 메일주소, 주소,닉네임, 나이,생일 등 |
- 암호화를 하면 데이터의 길이가 늘어나므로 데이터의 Length를 늘려주어야 한다. 실제 데이터의 Length가 암호화된 데이터에 길이보다 짧다면 짤려서 삽입된다.
MYSQL에서 지원하는 암호화 함수
단방향 암호화만 지원 | - MD5, PASSWORD. SHA1, SHA |
쌍방향 암호화 (암호화,복호화) 지원 |
- DES_ENCRYPT ,DES_DECRYPT - DECODE, ENCODE - COMPRESS, UNCOMPRESS |
ex) 단방향 암호화중 PASSWORD 함수 테스트
암호화
INSERT INTO 테이블명 (필드명) VALUES (PASSWORD('암호화 할 내용'))
복호화를 할 수 없기 때문에 조회시에는 해당 데이터를 암호화하여 컬럼에 데이터값과 비교하며 검증할 수 있다.
ex) 쌍방향 암호화 중 AES_ENCRYPT, AES_DECRYRT 함수를 통한 암호화, 복호화 테스트
삽입할 때에는 암호화된 바이너리 데이터를 HEX로 변환시켜 넣고, 가져올 때는 HEX를 풀고 복호화함.
암호화
INSERT INTO 테이블명 (필드명) VALUES (HEX(AES_ENCRYPT('문자열', '암호화 키')));
복호화
SELECT AES_DECRYPT(UNHEX(필드명), '암호화 키') FROM 테이블명;
================
로그인에 관련된 보안얘기를 하려고 합니다.
password(); // mysql.
md5(); // php.
crypt(); // php.
뭐, 암호화에 관련된 함수들이 여러 가지 있겠지만 위 3가지 함수는 범용적으로 많이들 쓰고 있고
안정성이 검증된 함수들이죠.. 그리고 모두 복호화가 안되거나, 어려운 해쉬함수들입니다.
흔히 password() 로 암호화시킨 비밀번호... 원래의 값을 절대 알 수 없다고들 표현합니다......
절대 알 수 없다 ?
절대 알 수 없다 ?
절대 알 수 없다 ?
id = 'abcd'
pw = '4ed0bdda4ee8f6a5'
위 pw 원래의 값을 과연 절대 알 수 없을까요 ? 정말 그럴까요 ?
password() 뿐 아니라, md5(), crypt() 등 해쉬함수들이 있는데요....
지금부터 제가 생각하고 있는 바를 차근차근 얘기해 나가려고 합니다....
그다지 어려운얘기 아니고요,
누구나 알 수 있는 쉬운 얘기들뿐입니다..
( 제가 워낙 쉬운 것만 좋아해서 쉬운 것밖에는 모릅니다 ^^ )
--------------------------------------------------------------------------------------
가령,
사용자가 비밀번호를 '3204' 라고 입력한 것을 md5() 로 해쉬시키면
' 640258597cbc50037072712f964cf5d8 ' 라는 값이 나오게 되지요...
md5() 는 디코딩을 지원하지 않는 단방향성 해쉬함수이기에 위 문자열을 보고 원래 값 '3204'를
추출 할 수는 없을 것입니다.
나머지 password(), crypt() 들도 마찬가지죠.. 상당히 어려운 알고리즘을 통해 해쉬된 문자열이
나오면서 원래의 값을 알아낼 수는 없게 됩니다.
특히 crypt() 는 두 번째에 주어지는 salt 값에 따라 똑같은 패스워드를 해쉬해도 결과는
매번 틀리게 됩니다...
가령,
$password = '20930';
$savepw = crypt( $password, md5( time() ) );
이런식으로 하면
실행 할 때마다 94JrlBxXigCZU , 30K4887zrFHBw , 등의 틀린 결과를 나오게 합니다.
모두 대단한 함수들입죠 ~~
------------------------- 그런데, 취약점은 항상 있는 법 --------------------------------
아이디 해킹을 하기위한 몇 가지 조건들이 있기 마련이죠..
그중, 해킹에 의해 DB가 노출되었다고 칩니다......
DB 비번관리를 잘못하던지, 디렉토리파싱을 당하던지 여러 이유로 DB가 노출되었다고 치구요..
DB 내용에는 사용자 계정 정보가
id = 'abcd'
pw = '640258597cbc50037072712f964cf5d8'
위처럼 되있다고 치구요.....
비밀번호가 암호화 되어있으니, 과연 안전할까요 ?
그런데 위 문자열 패턴을 보면, md5 로 해쉬된 것을 쉽게 알 수 있습니다.
지금부터 md5() 함수를 이용해서 원래의 비번을 찾아보도록 하겠습니다.
for( $pw = 0; $pw < 99999; $pw ++ ) {
if( '640258597cbc50037072712f964cf5d8' == md5( $pw ) ) {
echo "find ok : $pw ";
break;
}
}
// 출력 -> find ok : 3204
?>
간단하게 찾았습니다.
그러면, crypt() 는 안전 할까요 ??
아닙니다.. crypt() 역시 똑 같습니다..
노출된 DB 에.
id = 'abcd'
pw = '12FP8QJo.OCVg';
위 문자열 패턴을 보면, crypt() 로 해쉬된 것을 알 수 있습니다.
이것도 원래의 비번을 찾아보도록 하겠습니다.
for( $i = 0; $i < 99999; $i ++ ) {
if( '12FP8QJo.OCVg' == crypt( $i, '12FP8QJo.OCVg' ) ) {
echo " find ok : $i ";
break;
}
}
// 출력 -> find ok : 20930
?>
이번에도 결과가 나와 버립니다.
하지만 실제 암호는 숫자만 쓰는 것이 아니고, 문자열을 섞어서 입력하겠지요?
그렇다고 못 찾는 건 아닙니다.
.....
.....
$pw = $chk.chr($i);
if( '640258597cbc50037072712f964cf5d8' == md5( $pw ) ) {
.....
.....
}
$i ++;
if( $i > 126 ) $chk = ( ord( $chk ) + 1 );
.....
.....
저런 식으로 숫자 대신 문자를 자동으로 대입하게 해서 돌아가게 만들면 된다는 것이죠....
수행시간이 오래 걸린 것입니다만,,
문자 하나당 유효값이 93 자 인가? 그러니, 비밀번호가 4글자만 되도
loop 를 93 * 93 * 93 * 93 = 74,805,201 번을 돌아야 할 것입니다...
문제는 요즘 컴퓨터 성능이 워낙 좋아져서, loop 도는 시간이 그리 오래 걸리지도 않고,
또, 단위별로 끊어서 몇 단계 나누어서 병렬로 돌리면 결과는 더욱 빨리 나오게 된다는 것이죠.
그리고, 시간이 좀 걸려서 1주일쯤 걸렸다고 칩니다.
그 1주일동안 해커가 컴퓨터 앞에 매달려 있습니까 ? 잠을 못잡니까 ?
계속 지켜봐야 합니까 ?? 아니면 컴퓨터가 망가집니까 ?
그냥 디코드 돌려놓고 지 할일 하다가 결과 나오면 그때 해킹하는 것이죠...
------------------------------------------------------------------------------
결국은 DB 가 노출되거나, 해쉬된 비밀번호의 결과값이 노출되면 게임은 끝이라는 겁니다.
가령, 비밀번호를 암호화시켰다고 해서 비밀번호를 쿠키로 날아다니게한다던지,
페이지에 폼값으로 날려준다던지 하는 것은 해커에게 비밀번호를 다이렉트로 알려주는 것과
같습니다......
그럼,
비밀번호 안 날라 다니게 하고 DB노출만 막으면 안전할까요 ??
그것도 만만치 않습니다.
크라이언트에서 서버 접속해서 비밀번호 처음에 날려줄 때,,,,
네트웍을 감시당하면 아예, 원 비밀번호가 그대로 노출될 수 있습니다...
그래서 클라이언트에서도 JavaScript 를 이용해서 나름대로 암호화시킨 문자열을
날아다니게 하지요...
네트웍 감시를 당했을 때, 암호화된 비밀번호가 노출되는 것이구요..
하지만,, 이 역시 결과는 마찬가지죠.......
이 경우는 암호를 풀고 자시고도 없이
그냥 암호화된 비번 그 자체를 login 페이지로 날려서 그대로 접속해 버리면 되지요..
이 경우라면 원래의 암호는 알 필요도 없는 것이지요..
-------------------- 그럼 가장 안전한 방법은 무엇인가 ---------------------------------
개발자는 어떤 상황이던 비밀번호 스트링이 네트웍감시를 당하던, DB 가 노출되던 해서
해커에게 알려진다고 간주해야 합니다.
그럼 이러던 저러던 끝이라는 얘기일까요 ?
... 꼭 그렇지만은 않다고 봅니다.
여기서 우리는 보안을 위해 몇 가지 노력해야 할 것이 있습니다.
#-------------- 비밀번호가 어떤 모듈로 해쉬가 됐는지 모르게 하자.
가장 쉬운 방법은,, 암호화함수 1개에 의존하지 않고 여러 함수를 거쳐 2중 3중으로 돌려야 합니다.
#-------------- 외부에 노출이 안되는 서버만의 고유한 토큰키를 만들자.
가령,
사용자가 '1234' 라는 비밀번호를 쳤을 때 DB 저장할 때는 고유의 '키값'을 정해 눌러서 저장합니다.
예) 키값 '1011' + 비밀번호 '1234' = '2245'
저장도 '2245' 로 저장하고 로그인 인증 때도 또 키값을 더해서 인증하는 것이죠...
'키값'은 절대 DB 에 저장하지 않고, 파일로 접근하게 만듭니다.
서버 자체가 해킹당하기 전에는 절대 볼 수 없도록 실행권한과 접근권한도 설정 되야 겠지요..
#-------------- 클라이언트와 주고받는 비밀번호역시 또 다른 유일한 '키값'을 부여해서 주고받습니다...
서버쪽에서 DB 저장할 때와는 조금 틀리죠...
이 '키값'은 접속시마다 틀리게 합니다.
예) 키값 '0012'
클라이언트 '1234' + '0012' = '1246' ---- 송신 ----> 서버 '1246' - '0012' = '1234' 저장.
그런데,
주고 받을때 사용하는 키값은 클라이언트 페이지에 남아 있으면 안됩니다.
가령,
댓글 ( 4)
댓글 남기기