블로그 프로필 이미지





 CSRF 란? 


CSRF 란 Cross Site Request Forgery 의 약자로, 간단하게 설명하면, 웹싸이트의 취약점 공격의 하나이다. 자세한 뜻은 위키백과사전에 자세히 나와있기 때문에 그것을 참고하면된다. 솔직히 자기 수준에서 이해할려고 하는게 좋지 전문가의 수준에서는 이해할려고 하는건, 정신건강에 해롭다.


일반적으로 CSRF 는 사용자가 신뢰하고 있는 싸이트의 상태를 노리기 때문에 사용자의 입장에서는 정말 위험한 공격에 노출되어 있는것이다. 다시 말해, 신뢰하고 있는 A라는 싸이트에서 로그인하고, 사용자가 로그인상태에서 B라고 하는 공격자 싸이트 또는 게시물등을 이용하게됨으로 써 공격을 당하게 된다.


앞서 말했다시피 CSRF 같은경우 CSRF 이라는 키워드로 구글링을 하여도 많이 나오기 때문에, 여기서는 깊게 다루지는 않겠다.





 코드이그나이터의 CSRF 방어


코드이그나이터의 메뉴얼에는 폼헬퍼 의 form_open() 함수를 사용하면 폼의 CSRF 보호를 위한 hidden 필드가 자동으로 삽입된다고 나와있다. 하지만 나같은 경우 코드이그나이터의 HTML 과 폼에 관련된 헬퍼함수들은 사용하지 않기 때문에, 문제가 발생하게 된다. 솔직히 코드이그나이터의 HTML 과 폼 헬퍼는 순수 HTML 코딩으로 하는게 편한것같다. 


이곳에서는 form_open() 함수를 사용하지 않고 CSRF 방어를 적용하는 방법을 설명하겠다. 일단 아래의 경로처럼 자신의 config.php 파일 이 있는 경로로 이동하여 config.php 파일을 열어보도록 하자. 일반적으로 아래와 같은 경로에 위치해 있다.


application/config/config.php


위의 경로로 들어가 config.php 파일을 열였다면, CSRF 를 사용하기 위한 설정을 해주어야 한다. 설정은 아래와 같은 구문을 찾아 값을 변경하면된다.


<?PHP /* CSRF 방어를 사용할지 않할지 설정 | TRUE : 사용 | FALSE : 미사용 */ $config['csrf_protection'] = TRUE; /* CSRF 토큰의 이름을 설정 | input 요소의 속성 에서 name 에 해당되는 값이다. */ $config['csrf_token_name'] = 'ritn'; /* CSRF 쿠키의 이름을 설정 */ $config['csrf_cookie_name'] = 'RITOKEN'; /* CSRF 토큰의 생존기간이다. | 이 값은, csrf_regenerate 속성이 FALSE 일때만 유효하다. */ $config['csrf_expire'] = 1200; /* CSRF 토큰의 갱신 설정 | TRUE : 사용 | FALSE : 미사용 | 이값을 TRUE 로 사용하면, 매 요청시마다 토큰값이 변경된다. */ $config['csrf_regenerate'] = TRUE; /* CSRF 방어에서 제외할 URI 를 배열로 설정 | ex) http://www.redinfo.co.kr/main/siteinfo | url 이 위와 같다면 설정에는 아래와 같이 해주면 된다 | $config['csrf_exclude_uris'] = array('main/siteinfo'); */ $config['csrf_exclude_uris'] = array(); ?>


설정값들에 대한 설명은 코드이그나이터의 메뉴얼에서 설명할려고 했지만, 아직 CI 3.0 메뉴얼을 많이 읽어보지 못했기때문에, 주석으로나마 간단히 작성해 보았다. 


설정에서 보면 $config['csrf_regenerate'] 설정 부분이 있을것이다. 이 설정을 FALSE 로 해두어도 공격자가 임의 사용자의 토큰값을 알지 못하기 때문에 매 요청시 새로운 값으로 갱신이 되지 않더라도, 방어를 할 수 있다. 


코드이그나이터에서는 CSRF 방어에 사용되는 토큰값을 가져오기위해 아래와 같은 보안 클래스를 재공한다. 각각 클래스들의 설명은 주석을 보면된다.


<?PHP
/* 토큰의 값을 가져온다 */
$this->security->get_csrf_hash();

/* 토큰의 이름을 가져온다 */
$this->security->get_csrf_token_name();
?>


아래의 예제는 jquery의 ajajx 를 이용한 csrf 방어 예제이다. 주제가 코드이그나이터 이기 때문에, 코드이그나이터의 라이브러리 들이 사용이 되었다. 만약 코드이그나이터를 잘 모른다면, 아래의 예제를 이해하기 힘들 수 있다.


 application/views/csrf.php


<!doctype html> <html> <head> <meta charset="utf-8"> <title>코드이그나이터를 이용한 CSRF 방어 예제</title> <script src="http://code.jquery.com/jquery-1.11.2.min.js"></script> <script> $(document).ready(function(e) { $('#form').submit(function(){ $.ajax({ url:$(this).attr('action'), type:$(this).attr('method'), data:$(this).serialize(), success: function(data){ /* ajax 의 결과값을 처리 */ $('#ajax_result').html(data); /* csrf 갱신값을 처리 */ $('#csrf').val($('#token').data('value')); }, error:function(xhr, status, error){ /* 에러 출력 */ alert('error'); } }); return false; }) }); </script>

</head> <body> <div id="ajax_result"> </div> <form id="form" method="post" action="/ajax/test/"> <input type="hidden" id="csrf" name="<?=$this->security->get_csrf_hash();?>" value="<?=$this->security->get_csrf_token_name();?>"/>

<input type="text" name="text" placeholder="테스트 값을 입력" /> <input type="submit" value="전송"/> </body> </html>


 application/controllers/Ajax.php


<?PHP defined('BASEPATH') OR exit('No direct script access allowed'); class Ajax extends CI_Controller { public function test() { /* post 로 전송된 text 필드의 값을 출력 */ echo "테스트값 : ".$this->input->post('text',true); /* 토큰값 갱신 설정($config['csrf_regenerate'])이 TRUE 일경우, 요청시마다, 토큰값을 | 갱신을 해주어야 하기때문에 아래와 같이 레이아웃을 이용하여 데이터에 토큰값을 생성해준다. */ echo "<span id='token' data-value=".$this->security->get_csrf_hash()."></span>"; } } ?>


위의 예제를 본인의 웹싸이트에 맞게 설정을 하였다면, 테스트시 아래와 같은 결과를 얻을 수 있다. 아래는 텍스트 입력 필드에, ajax 란값을 입력후 전송 버튼을 누른 결과이다. 



(위의 예제 실행결과)


CSRF 방어 같은경우 내부적으로 처리되기때문에, 실행결과는 그렇게 중요한 화면이 아닌것같다. 여기서 중요한건 ajax 방식은 페이지 이동이 없기때문에, CSRF 방어를 위한 설정에서  $config['csrf_regenerate'] 값을 TRUE 로 설정하였다면, AJAX의 결과값을 처리할시 다음번 요청을 위해 별도로 CSRF 토큰 값을 갱신해 주어야 한다는것이다. 


만약, AJAX 값을 처리할 시 CSRF 토큰값의 갱신이 이루어 지지 않는다면, 다음번 요청시에는 값을 받아볼 수 없을것이다. 하지만, CSRF 방어의 예외처리 상황도 발생할 수 있다. CSRF 방어의 예외처리같은경우 CSRF 설정중에서 $config['csrf_exclude_uris'] 부분을 아래와 같은 형식으로 설정하면된다.


$config['csrf_exclude_uris'] = array('main/siteinfo');

 

위의 설정에서 array 의 값을 보면, main/siteinfo 라는 값이 설정되어있다. 이값은 코드이그나이터에서 기본 URI 규칙을 사용할 시 적용되는 값들이다. 즉 위의 URI 값은 호스트명을 제외한 uri 값이라고 생각하면된다. 만약 아래와 같은 싸이트 주소가 있다고 가정하면, 예외 처리를 해줄 URI 설정은 위의 설정값과 같이 된다.


http://www.redinfo.co.kr/main/siteinfo


CSRF 설정에서 예외처리가된 URI 들은 CSRF 방어에 대한 영향을 받지 않기 때문에, 자유롭게 코딩할 수 있다. 이 설정은 상당히 유용하다. 그이유는, CSRF 방어가 필요없는 부분에서 항상 사용해야하는 불편함을 없애주기 때문이다.

댓글
(비회원은 자신의 글을 볼 수 없습니다.)