최근들어 큰 프로젝트들이 많이 겹쳐 블로그 운영을 많이 못했던것같다. 현재 UI도 리뉴얼 구성중에 있긴하나 당분간은 엄두가 나지 않을듯 싶다.
이번에 소개할 내용은 보안토큰을 적용한 간단한 FORM 전송 예제이며 이 방법을 주로 사용하는 목적은 스팸(악성)글을 막기위한 용도로 많이 사용된다. 자세한 방법은 아래 예제 소스를 통해 알아보자.
<?php
// session_start(); // 세션이 자동시작이 아닐경우 주석해제 후 사용
$secureKey = md5('SECURE_KEY'.mt_rand(111111111,999999999)); // 비밀키 -> 고정키+time
$_SESSION['secureKey'] = $secureKey; // 세션으로 비밀키를 추가해준다.
$time = md5(time()); // 타임
$token = md5($secureKey.$time); // 토큰
?>
<form name="form" action="/@ex/token-ex-exe" method="post" onsubmit="return formSubmit();">
<input type="hidden" name="time" value="<?php echo $time; ?>">
<input type="hidden" name="token" value="<?php echo $token; ?>">
<h1>사용자 인증 예제</h1>
<p><input type="text" name="name" id="name" value="" placeholder="이름"></p>
<p><input type="text" name="birthday" id="birthday" value="" placeholder="생년월일"><p>
<p><input type="submit" value="전송"><p>
</form>
<script>
function formSubmit(){
var name = document.getElementById('name').value;
var birthday = document.getElementById('birthday').value;
if( name == ''){ alert('이름을 입력해주세요.'); return false; }
if( birthday == ''){ alert('생년월일을 입력해주세요.'); return false; }
return true;
}
</script>
그다음 FORM 전송시에는 input 의 hidden 값으로 time 값과 $secureKey 값을 통해 생성된 token 값을 넣어주었다. 이 값들은 데이터 목적에 따라 달라질 수 있다. 예를 들면 쇼핑몰 주문 데이터를 전송하는 프로그램이라면 주문번호가 있을 경우 time 대신 주문번호와 주문일자를 함께 암호화를 해주면 보안을 더 강화할 수 있다.
<?php
try{
// session_start(); // 세션이 자동시작이 아닐경우 주석해제 후 사용
// 비킬키 세션값을 체크
if( empty($_SESSION['secureKey'])){ throw new Exception("인증이 올바르지 않습니다."); }
// 비밀키 세션값을 변수에 담는다.
$secureKey = $_SESSION['secureKey'];
// 사용된 비밀키 세션값은 삭제한다. (연속 실행 방지)
unset($_SESSION['secureKey']);
// 예외처리시에는 공격자가 예측할 수 없게 예외처리 문구를 모두 통일시킨다.
if( empty($_POST['time'])){ throw new Exception("인증이 올바르지 않습니다."); }
if( empty($_POST['token'])){ throw new Exception("인증이 올바르지 않습니다."); }
if( empty($_POST['name'])){ throw new Exception("인증이 올바르지 않습니다."); }
if( empty($_POST['birthday'])){ throw new Exception("인증이 올바르지 않습니다."); }
// 값들을 변수화
$time = $_POST['time'];
$token = $_POST['token'];
$name = $_POST['name'];
$birthday = $_POST['birthday'];
// 체크할 토큰값을 정의
$checkToken = md5($secureKey.$time);
// 토큰값 비교
if( $token != $checkToken){ throw new Exception("인증이 올바르지 않습니다."); }
// 출력
echo '이름: '.$name;
echo '<br/>';
echo '생년월일: '.$birthday;
echo '<br/>';
}
catch(Exception $e){
die($e->getMessage());
}
실제 회사 고객사 스팸(악성) 글이 갑자기 올라와서 빠르게 막기위해 위와 같이 세션으로 토큰만 구성을 하여 금방 막을 수 있게되었다. 물론 앙심을 품고 수동으로 직접 폼 데이터를 입력해서 공격한다면 이보다 더 강화된 구글 리캡챠와 같은 라이브러리를 사용해야 하지만 앙심을 품은 유저는 어떠한 방법으로도 막기는 어렵기 때문에 그런 상황이 올 경우 가장 좋은 방법은 IP 차단이다. 이경우 가장 빠른 방법은 DB에 IP를 별도로 기록하고 연속된 공격자를 찾아내서 단일IP 또는 IP 내역대를 차단해 버리는 방법이 가장 좋다.
위와 같이 보안토큰을 이용하여 FORM 데이터를 안전하게 전송하는 방법에 대해 알아보았다. 이는 실무에서 사용할 수 있는 방법이긴 하나 실제 결제 또는 거래가 발생되는 사이트는 DB를 활용하여 URL에 보안토큰을 발급하고 해당 보안 토큰의 유효기간 그리고 시도 횟수등을 업데이트하도록 구성하여 유효기간 또는 N번 횟수 시도가 발생될 경우 만료처리를 하여 접근을 막는방법이 있다. 보통 스팸(악성) 공격자들은 게시판과 같은 구간에 자동 프로그램을 돌려 공격하기 때문에 위와 같은 방법으로도 어느정도 해결할 수 있으니 상황에 맞게 사용할 수 있도록 하자.