‘php’ 에 새로 입문하는 사람으로서, ‘password()’ 함수를 이용하여 결과를 암호화 해두는 방법에 대한 자료를 찾는 것이 힘들었다.
글쎄, 모두 그렇지 않겠지만, 여전히 ‘ASP’ 와 ‘MS-SQL’을 이용한 웹 프로그램 에서는 별도로 비밀번호를 암호화 하지는 않는다.
또한, ‘주민등록번호’ 역시 별도로 암호화를 해두지 않는데, ‘MySQL’ 에서는 오래전부터 이런 고민들에 대해 방법론이 제시되었던 모양이다.
자 그렇다면, ‘password()’를 이용해 암호화한 값을 어떻게 저장하며, 나중에 ‘회원로그인’시 어떻게 비교하는가?
각종 무료게시판과 가지고 있던 자료를 여기저기 뒤져보았는데, 명쾌하게 답을 보여주는 곳은 없었다.
방법은 의외로 간단하다.
‘MySQL’은 ‘password()’ 함수를 지원하는데, 이를 이용해 넘겨지는 값을 자체적으로 암호화하여 저장한다.
그런데, 이를 다시 복호화(암호화된 정보를 푸는) 하는 로직은 없다.
찾아본 정보에 따르면, 애초부터 복호화를 할 생각이 없었기 때문에 그런 함수가 존재하지 않는다는 것이다.
그렇다면, ‘password()’를 통해서 암호화해둔 값과 로그인시 입력한 비밀번호를 어떻게 대조할까?
그것은 아래와 같은 방법으로 가능하다.
SELECT * FROM user_table WHERE id = '$id' AND pwd = PASSWORD('$pwd');
즉, 비밀번호 필드인 ‘pwd’ 필드와 대조할 때, 로그인시 입력한 비밀번호를 ‘password()’를 이용해 다시 변환하여 변환된 두 값을 대조하는 것이다.
이외에, 단지 ‘password()’를 통하면 값이 어떻게 변환 되는지 알아보는 방법은 아래와 같은 구문으로 가능하다.
$value = "0000";
$ff = mysql_query(" select password('$value') as pass ");
$r = mysql_fetch_array($ff);
echo $r[pass];
단지 쿼리를 날려서 어떻게 값이 변환되는지 확인할 수 있다.
저장된 값을 확인해보자.
$x = "0000";
mysql_query("INSERT INTO test2 (f1,f2) VALUES ('$x',password('$x'))");
라는 구문을 통해, 변환되기 전의 값과 변환된 값을 모두 비교할 수 있는 쿼리를 이용해 새로운 레코드를 생성해본다.
웹에서 자료를 더 검색해보니, 아마도 구 버전의 ‘MySQL’ 과 신 버전의 ‘MySQL’ 에서 각각 암호화한 결과 값에 차이가 있는 모양이다.
내 경우에는 ‘APM_Setup6’를 설치하여, ‘MySQL5’ 버전을 설치한 경우이다.
이렇게 구 버전과 호환에 문제가 발생할 경우에는, ‘old_password()’ 함수를 이용해서 비교하면 된다고 한다.
같은 버전대의 ‘MySQL’에서 암호화된 정보의 경우에는 같은 값에 대해서 암호화된 결과 값이 동일했다.
즉, 내 컴퓨터에서만 ‘0000’ 이 ‘19225735263cd3c5’로 변환되는 것이 아니라, 다른 컴퓨터에 설치된 ‘MySQL’ 에서도 ‘password('0000')’ 를 통해 암호화된 값이 동일하다는 것이다.
(PS. 암호화를 통해 어떤 값으로 변환되는지 이미 정해져 있기 때문에, 이에 대한 데이터베이스를 만들면 원래의 값이 무엇인지 역으로 추적하는 방법이 가능하다.)
여기서 의문이 생긴다.
과연 이런 암호화 과정이 왜 필요할까?
어떤 보안문제 때문에 이런 과정을 거치게 한 것일까?
‘password()’를 통해서 회원 비밀번호나 주민등록번호를 등록해두면, 원래의 값이 무엇인지 알아낼 도리가 없다.
물론, ‘복호화(암호화된 정보를 푸는)’하는 로직이 있기는 있어서 4자리 정도까지는 몇 초 만에 암호화된 값의 원래 값이 무엇인지 복호화 하는 것이 가능하지만, 6자리를 넘어가면 엄청나게 오랜 시간이 걸린다고 한다.
이론적으로는, 결국 복호화도 가능하다는 것이다.
다른 측면에서 얘기를 해보자면, 원론적으로는 ‘PHP’에서 사용하는 ‘password()’ 함수를 사용하는 방식은 처리하는 값이 암호화되기 때문에 접근성이 용이하지 않다.
심지어 프로그래머 자신도 암호화되기 전의 원래 값이 무엇인지 알아내는 것이 쉽지 않다.
이로 인해, 간혹 회원이 비밀번호를 잊어버려서 문의를 해올 때, 암호화 되기 전의 원래의 비밀번호를 알 방법이 없어 회원에게 비밀번호를 알려줄 수 없다.
그래서 임시 로그인 비밀번호를 부여한 후 사용자에게 그 값을 통보해주면, 사용자가 임시 비밀번호로 로그인하여 다시 비밀번호를 바꾸도록 유도하고 있다.
또한, 주민등록번호 같은 정보를 ‘password()’ 함수로 변환하여 관리할 경우, 원래의 주민등록번호가 무엇인지 알 수 없기 때문에, 최악의 경우, 회원이 고객센터에 전화를 걸어 “내 주민등록 번호가 0000 인데~” 라고 말을 해줘도 그 주민등록번호가 회원정보에 입력되어 있는 값과 일치하는지 확인 할 방법이 없다.
즉, 고객센터에서 고객 응대를 제대로 하기 어렵다.
물론, 고객센터에 전화를 걸어 온 고객이 말해 준 주민등록 번호를 ‘password()’ 함수를 이용해 변환하여 그 값이 DB 에 기록된 암호화된 값과 일치하는지 비교하는 별도의 페이지를 만든다면 고객센터에서 원래의 값과 동일한지 비교하는 것이 가능하기는 하겠다.
(물론, 고객이 고객센터에 자신의 주민등록번호를 알려주는 방식의 서비스는 되도록 지양되어야 한다.)
이런 불편함 때문에(오히려 보안성이 높다고 봐야 할까?), 왜 이런 암호화 방법을 사용하여 유지보수가 불편해지도록 하는가에 대한 의문이 생겼다.
그런 의문이 생긴 이유는, 암호화된 값을 원래 값으로 되돌리는 ‘복호화(암호화된 값을 다시 푸는)’를 지원하지 않는다는 점 때문이었다.
프로그래머나 혹은 서비스 유지보수를 담당하는 입장에서, 단지 그런 복호화의 불편함이 문제라면, 별도로 자신만의 암호화 알고리즘을 만들어서 운용할 수도 있을 것이다.
그러면, 암호화된 정보를 쉽게 풀어낼 수 있는 로직을 이미 가지고 있으니, 이런 운용상의 불편함은 사라질 것이다.
여기서 한 가지 문제를 더 생각해 보자.
‘PHP’ 에서는 왜 ‘password()’의 복호화 함수를 지원하지 않느냐 하는 점이다.
어찌되었건 공개소프트웨어이고, 많은 사람들이 무료로 사용하도록 공개되다 보니, 복호화 알고리즘이 존재한다면 암호화 알고리즘 자체가 무의미해질 가능성이 있다.
아마도, 그런 이유 때문이 아니었을까?
즉, 애초부터 복호화 알고리즘을 제공하지 않아서 암호화 로직의 보안성이 높아지는 장점이 있다.
다른 면에서 생각해보자,
과연 ‘password()’를 사용하는 방식이 어떠한 보안상의 장점이 있을까?
‘sql 쿼리’를 가로채는 경우를 방지할 수 있을까?
그러나 ‘php’ 에서 ‘sql 쿼리’를 날릴 때, ‘password('0000')’ 으로 날린다면, 쿼리에 이미 암호화되기 전의 원래 값이 노출되기 때문에 보안성이 높다고 볼 수 없다.
즉, 중간에 쿼리를 가로채는 공격에 대해서는 속수무책이다.
그런 공격을 대비한 것은 아니고, 단지 데이터베이스에 저장되는 값을 암호화하여 추후에 데이터베이스에 접근한 공격자가 데이터를 획득하더라도 이 값을 쉽게 분석하지 못하게 하는 정도의 보안성을 가지고 있다.
그런데 재미있는 점은, 기존에 많이 사용되고 있는 무료 게시판 ‘그누보드’나 ‘제로보드XE’ 의 데이터베이스 필드속성(비밀번호 필드)을 확인해본 결과, ‘그누보드4’의 경우에는 ‘varchar(255)’ 속성이고, ‘제로보드XE’ 의 경우에는 ‘varchar(60)’ 이었다.
필드 자체는 따로 보안 속성을 가지고 있지 않은 평범한 일반텍스트 필드라는 점이다.
그래서 만약, 데이터베이스가 노출되었다면(혹은 데이터가 유출되었다면), 암호화된 필드 값은 쉽게 얻을 수 있다.
그리고 만약 ‘php’ 페이지를 조작할 수 있다면, 굳이 ‘password()’ 로 복잡한 알고리즘을 짤 필요 없이, 단지 암호화된 값을 직접 필드 값과 대조해도 똑같은 결과를 얻을 수 있다는 것이다.
필드 자체가 암호화 필드가 아니기 때문이다.
이와 비교하여, (내 기억으로는) ‘MS-SQL’ 의 경우에 ‘password’ 필드를 제공한다.
여기저기 뒤져보아도, ‘MySQL’ 에는 ‘password’ 타입의 필드는 없는 것 같다.
하지만, 필드 타입으로 ‘password’ 타입을 제공한다 하더라도, 결국 사용자가 입력한 값을 비교하려면 원래 입력된 값이 노출될 수밖에 없다.
즉, ‘php’ 페이지를 조작할 수 있거나, 데이터베이스가 노출된 상태에서는 어떤 보안유지도 불가능 하다는 점에서 굳이 ‘password()’를 통한 암호화가 얼마나 보안성이 있겠는가 하는 의문으로 다시 되돌아 왔다.
PS.
일부에서는, 자신이 만든 로직을 이용해 우선 일차적으로 암호화를 하고, 다시 ‘PHP’ 자체에서 제공하는 ‘password()’ 함수를 이용해 2차로 암호화 하는 방법을 쓰기도 하는 모양이다.