Loading...
보안토큰을 적용하여 안전하게 FORM 전송을 하는 간단한 예제
REDINFO
몇주 전 2024-05-05 20:21:55

최근들어 큰 프로젝트들이 많이 겹쳐 블로그 운영을 많이 못했던것같다. 현재 UI도 리뉴얼 구성중에 있긴하나 당분간은 엄두가 나지 않을듯 싶다. 

 

이번에 소개할 내용은 보안토큰을 적용한 간단한 FORM 전송 예제이며 이 방법을 주로 사용하는 목적은 스팸(악성)글을 막기위한 용도로 많이 사용된다.  자세한 방법은 아래 예제 소스를 통해 알아보자.

 

EX) 폼 파일 구성 (form.php)
<?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>
위의 예제에서 중요한 부분은 파일 상단에 PHP로 선언된 $secureKey, $time,  $token 값이다. 이 값들은 활용목적에 따라 변경해서 사용하면된다. 특히 $secureKey 값은 상황에 따라 주기적으로 변경해주면 보안을 더  강화할 수 있고, secureKey 값을 세션도 함께 사용되었는데 이는 한번 전송된 FORM 데이터가 연속적으로 실행되는것을 방지 하기 위한 목적이다. 이는 폼 처리 파일 구성에서 좀더 자세히 실펴보도록 하자. 

그다음 FORM 전송시에는 input 의 hidden 값으로 time 값과 $secureKey 값을 통해 생성된 token 값을 넣어주었다. 이 값들은 데이터 목적에 따라 달라질 수 있다. 예를 들면 쇼핑몰 주문 데이터를 전송하는 프로그램이라면 주문번호가 있을 경우 time 대신 주문번호와 주문일자를 함께 암호화를 해주면 보안을 더 강화할 수 있다. 

 

EX) 폼 처리 파일 구성 (exe.php)
<?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());
}
FORM 페이지로 부터 전송된 데이터를 처리하는 페이지에서는 넘겨받은 토큰 데이터를 비교하는 구간을 추가하여 보안을 한층 더 강화시킬 수 있다. 이렇게만 해도 공격자는 어떠한 방법을 쓰더라도 $secureKey 값을 알 수 없는한 FORM 페이지를 무조건 경유해야한다. 특히 앞서 세션으로 비밀키값을 념겨주었는데 이는 1회성으로 사용하여 폼 처리 페이지를 연속 실행하여 공격하는것을 방지하기위한 목적에 있다. 

실제 회사 고객사 스팸(악성) 글이 갑자기 올라와서 빠르게 막기위해 위와 같이 세션으로 토큰만 구성을 하여 금방 막을 수 있게되었다. 물론 앙심을 품고 수동으로 직접 폼 데이터를 입력해서 공격한다면 이보다 더 강화된 구글 리캡챠와 같은 라이브러리를 사용해야 하지만 앙심을 품은 유저는 어떠한 방법으로도 막기는 어렵기 때문에 그런 상황이 올 경우 가장 좋은 방법은 IP 차단이다. 이경우 가장 빠른 방법은 DB에 IP를 별도로 기록하고 연속된 공격자를 찾아내서 단일IP 또는 IP 내역대를 차단해 버리는 방법이 가장 좋다. 

 

위와 같이 보안토큰을 이용하여 FORM 데이터를 안전하게 전송하는 방법에 대해 알아보았다. 이는 실무에서 사용할 수 있는 방법이긴 하나 실제 결제 또는 거래가 발생되는 사이트는 DB를 활용하여 URL에 보안토큰을 발급하고 해당 보안 토큰의 유효기간 그리고 시도 횟수등을 업데이트하도록 구성하여 유효기간 또는 N번 횟수 시도가 발생될 경우 만료처리를 하여 접근을 막는방법이 있다.  보통 스팸(악성) 공격자들은 게시판과 같은 구간에 자동 프로그램을 돌려 공격하기 때문에 위와 같은 방법으로도 어느정도 해결할 수 있으니 상황에 맞게 사용할 수 있도록 하자. 

이 포스트글이 도움이 되었나요?
2
카테고리 연관글

Comment

댓글작성

0(500) | 1(30)