최종보고서 데이터베이스의취약점분석과해결책 제출일자 : 2012년 5월 00일과목명 : 캡스톤디자인팀명 : DIS 팀장 : 강연준팀원 : 90816542 강연준 90710000 조응철담당교수 : 양환석교수님
목 차 1. 서론 1.1. 연구의필요성 1.1.1 데이터베이스보안의개념 1.1.2 데이터베이스보안의필요성 1.2. 팀구성원 1.2.1 구성원 1.3. 주간활동보고서 1.3.1 조별주간활동보고서 2. 관련연구 2.1. SQL Injection 2.1.1 SQL Injection 의정의 2.1.1 SQL Injection 공격유형 2.1.2 SQL Injection 취약점확인 2.1.3 취약성판단 2.2 PHP 2.3 Apache 3. 테스트결과 3.1. 웹사이트구축 3.1.1 PHP 웹사이트구축 3.2. SQL Injection 대응방안 3.2.1 SQL Injection 공격 3.2.1 SQL Injectinon 대응방안적용 4. 결론 5. 부록 6. 참고문헌 7. 발표 PPT 2
1. 서론 1.1. 연구의필요성 1.1.1 데이터베이스보안의개념데이터베이스보안 (Database Security) 은의도하지않은활동으로부터데이터베이스를보호하는시스템, 프로세스, 프로시저이다. 의도한것이아닌활동은권한오용, 악의있는공격또는부주의한관리로분류될수있다. 데이터베이스보안을평가하는중요한절차는데이터베이스에대한취약점점검을수행하는것이다. 취약점점검은데이터베이스침입에사용될수있는취약부분을찾는것이다. 데이터베이스관리자나정보보안관리자는데이터베이스소프트웨어내에알려진취약점과더불어위에언급된계층간통제구성의오류를찾기위해취약점스캔을실시한다. 스캔결과는침입자들의위협을줄이고데이터베이스를견고히하기위해사용되어야한다. 1.1.2 데이터베이스보안의필요성데이터베이스는우리가매일가장많이사용하고있는어플리케이션중의하나이다. 데이터베이스보안은크게접근제어 (access control), 감사 (auditing), 인증 (authentication), 암호화 (encryption), 무결성 (integrity) 등많은주제를포함하고있다. 본작품에서는위주제중인증에국한하여데이터베이스보안문제를다루고자한다. 현재많은어플리케이션이데이터베이스와연동을해야하는데이를손쉽게하기위해개발된스크립트언어가 PHP 이다. PHP에서는데이터베이스연결에필요한정보를다로하나의파일 (DB connect) 로모아서일일이데이터베이스에연결해주는코드를쓸필요없이바로 DB connect 파일을포함함으로써데이터베이스연결을한다. 그런데이 DB Connect 파일은정상적인프로그램에의해서쓰일뿐만아니라, 만일누구나접근가능한루트디렉토리에놓이게되면공격자가이를악용할수있다는문제가있다. 본작품에서는보안이취약한가상사이트환경을구축하고이의문제점을분석하고보완할수있는방안을제시한다. < 오라클사에서발표한데이터베이스취약점공격빈도수 > 3
1.2. 팀구성원 1.2.1 구성원 2 조 구성원 역할분담 조장 강연준 웹페이지구축, 데이터베이스구축, SQL Injection 대응방안연구및적용, 전체총괄 조원 조응철 자료수집 1.2. 주간활동보고서 1.2.1 조별주간활동보고서 주기 내용 주간활동사항 1 주차 조원의역할분담및일정계획, 자료수집 2 주차 3 주차 기초적인데이터베이스지식습득 4 주차 데이터베이스의 구조구상및설게구축 5 주차 6 주차 웹페이지구축을위한지식습득및구축 7 주차 웹페이지과데이터베이스연동 4
8 주차 취약점에대한 자료수집과분석 9 주차 취약점에대한대응방안연구 10 주차 중간점검 11 주차 취약점에대한대응방안연구 12 주차 취약점에대응방안적용 13 주차 소스내오류검토및수정 14 주차 발표준비및선테스트 15 주차 제작발표 5
2. 관련연구 2.1. SQL injection 2.1.1 SQL Injection의정의현재대부분의웹사이트들은사용자로부터입력받은값을이용해데이터베이스접근을위한 SQL Query를만들고있다. 사용자로그인과정을예로들면, 사용자가유효한계정과패스워드를입력했는지확인하기위해사용자계정과패스워드에관한 SQL Query문을만든다. 이때 SQL injection 기법을통해서정상적인 SQL query를변조할수있도록조작된사용자이름과패스워드를보내정상적인동작을방해할수있다. 입력문에구조화조회언어 (SQL) 문에대한필터링이없을경우해커가 SQL문으로해석될수있는입력을시도하여데이터베이스에접근할수있는보호취약점. 웹브라우저주소 (URL) 창또는사용자 ID 및패스워드입력화면등에서데이터베이스 SQL문에사용되는문자기호 ( 및 ) 의입력을적절히필터링하지않은경우에해커가 SQL문으로해석될수있도록조작한입력으로데이터베이스를인증절차없이접근, 자료를무단유출하거나변조할수있다. 예를들어관리자 ID와패스워드에아래문자열을입력했을때로그인되면취약점이존재한다. 2.1.1 SQL Injection 공격유형 ⅰ) 사용자인증을비정상적으로통과할수있다. ⅱ) 데이터베이스에저장된데이터를임의로열람할수있다. ⅲ) 데이터베이스의시스템명령을이용하여시스템조작이가능하다. 이러한취약점을 SQL Injection 취약점이라고하며, 사용자가데이터입력이가능한수많은 웹페이지상에이러한취약점이존재할수있다. 2.1.2 취약성판단 ⅰ) 검색어필드및로그인 ID, PASSWD 필드에큰따옴표 ("), 작은따옴표 ('), 세미콜론 (;) 등을입력한후, DB error가일어나는지확인한다. ⅱ) 로그인모듈점검 Oracle인경우 : ID 필드에 ['or 1=1 --], 비밀번호필드에는아무값이나입력한후로그인을시도한다. 기타의경우 ID 필드에 ['or ''='], 비밀번호필드에 ['or ''='] 을입력한후로그인을시도한다. 위방법외에도다양한방법이가능하기때문에, 로그인및사용자입력값을사용하는 소스에서 DB Query 생성방식을직접점검해야한다. 6
< 그림 2 > SQL Injection 2.2 PHP 2.2.1 PHP의정의 PHP란하이퍼텍스트생성언어 (HTML) 에포함되어동작하는스크립팅언어. 별도의실행파일을만들필요없이 HTML 문서안에직접포함시켜사용하며, C, 자바, 펄언어등에서많은문장형식을준용하고있어동적인웹문서를빠르고쉽게작성할수있다. ASP(Active Server Pages) 와같이스크립트에따라내용이다양해서동적 HTML 처리속도가빠르며, PHP 스크립트가포함된 HTML 페이지에는.php,.php3,.phtml이붙는파일이름이부여된다. 처음에는 Personal Home Page Tools 이라불렸으며, 공개된무료소스이다. 2.3 Apache 2.3.1 Apache란? 1995년처음발표된월드와이드웹 (WWW:World Wide Web) 서버용소프트웨어이다. NCSA(National Center for Supercomputing Applications: 미국국립수퍼컴퓨터활용센터 ) 소속개발자들이개발한 NCSA httpd 1.3 웹서버를자신들이개량한것으로소스코드까지공개되고있다. NCSA httpd 1.3 서버에패치 (patch) 파일을제공했던개발자들이 'A PAtCH server' 라는용어 에서아파치라는이름을따왔다. 1995 년 3 월 18 일공개된아파치 0.2 가 NCSA httpd 1.3 에패 치파일을제공하였다. 패치파일을꾸준히개선해제공하고있으며, 최고수준의성능을발휘하기때문에월드와이드웹서버용소프트웨어로가장많이사용되고있다. 오픈소스 (open source) 라이선스에따라무료로배포되어원하는사람들이자유롭게사용할수있다. 유닉스 윈도등을비롯해거의모든운영체제와시스템에서운용이가능하다 7
3. 테스트결과 3.1. 웹사이트구축 3.1.1. PHP 웹사이트구축 ⅰ) HTML 과 PHP 를이용하여웹사이트를구축하였다. 로그인, 회원가입, 수강조회, 성적조회메뉴란을만들었다. < 구축된웹사이트 > ⅱ) 로그인페이지에서정상적인아이디와비밀번호를입력하고로그인한다. < 로그인페이지 > 8
ⅲ) 아이디와비밀번호가일치할경우정상적으로로그인이된다. < 정상적인방법으로로그인을하였을경우 > ⅳ) 아이디와비밀번호가일치하지않을경우에경고메시지를띄워주고다시로그인 페이지로돌아가게된다. < 아이디가올바르지않을경우 > < 비밀번호가올바르지않을경우 > 9
3.2. SQL Injection 대응방안적용 3.2.1. SQL Injection 공격 ⅰ) SQL Injection 공격을실행하기위해서본문에설명한바와같이 or 1=1--' 이라는문구를입력하고로그인을시도한다. 아이디입력부분에문구를넣는방법과비밀번호에문구를넣는방법이두가지방법을통하여테스트해보았다. < 아이디와비밀번호를모르는상태에서의 SQL Injection 공격 > < 아이디와비밀번호를모르는상태에서의 SQL Injection 공격 > ⅱ) SQL 쿼리문을파괴하는문구를넣고로그인을시도한결과로아이디와비밀번호가 일치하지않았음에도불구하고로그인되는것을확인할수있다. < 취약점을파고들어아이디와비밀번호가일치하지않음에도로그인이되었다. > 10
3.3. SQL Injection 대응방안적용 3.3.1. SQL Injection 공격의대응방안적용 SQL Injection 공격의대응방안으로는두가지방안이있다. 그중하나는데이터베이스내의프로시저를통하여입력받은값을프로시저내에서치환함수를통해필터링하여구문이변조, 파괴가안되도록하는방법이있고, 또다른방법으로는 PHP소스내에서입력받은파라미터값을앞에서설명한바와같이치환방법을통하여필터링을해주는것이다. 3.3.2. 대응방안적용 ⅰ) 데이터베이스와연동을하는스크립트의모든파라미터들을점검하여사용자의입력값이 SQL injection을발생시키지않도록수정한다. ⅱ) 사용자입력이 SQL injection을발생시키지않도록사용자입력시특수문자 (' " / \ ; : Space -- + 등 ) 가포함되어있는지검사하여허용되지않은문자열이나문자가포함된경우에는에러로처리한다. < 입력받은값을 PHP 내에서치환해주는함수 > 아이디와패스워드에입력받은값이치환함수를거쳐다른기호로바뀌게되고 데이터베이스와일치하지않기에로그인이거부가되는것을알수있다. 11
4. 결론 본작품에서 or 1=1 -- 라는문구를넣어서쿼리문을의도적으로 ture 의값이되도록하였고그결과로그인을강제적으로되었다. 만약로그인된아이디가웹사이트를관리하는관리자계정이라면웹사이트를통제할수있는대부분의권한을갖게될수있다. 그리고본작품에서보여지진않았지만 update, delete, union 등여러가지방법으로 Injection 공격을해올경우데이터베이스안에있는정보의변조, 파괴, 엄청난피해가발생할수있다. 모든프로그램언어그리고 SQL DB는잠재적인취약점을가지고있다. 이를보호하기위해서는강력한디자인, 정확한입력값검증, 견고하게서버를운영해야한다. 견고하게서버를운영하기위해서는, DB 최소권한의유저로운영해야하고사용하지않는저장된프로시저와기능들은제거하거나관리자에게제한된접근권한을주어야한다. 또한모든사용자계정의패스워드를강화시켜야하며웹서버접근만허용해야할것이다. 또한 SQL 서버의에러메시지를사용자에게보여주지않도록설정한다. 공격자는리턴되는에러메시지에대한분석을통하여공격에성공할수있는 SQL Injection 스트링을알아낼수있다. 따라서 SQL 서버의에러메시지를외부에제공하지않도록한다. 세계적으로무수히많은기업들이있다. 그무수히많은기업들중데이터베이스를사용하지않는기업은없다고봐도무방하다그만큼데이터베이스의의존도가높은사회이고각기업마다데이터베이스에기업의기밀정보를포함하여많은정보를저장해두고사용하고있다. 그만큼데이터베이스의보안은중요하고보안이중요한만큼그데이터베이스의중요성을다루고있다. 이작품을통하여우리팀에겐데이터베이스보안에대한경각심을심어주고더욱더관심을가질수있는계기가되었고이작품을보는이에겐조금더경각심을심어주고관심을가질수있는계기가되었길바래본다. 12
5. 부록 웹사이트전체페이지 html 소스 index.php <html> <head> <meta HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=utf-8"> <title>:: DIS 졸업작품 ::</title> </head> <frameset framespacing="0" border="0" frameborder="0" rows="210,*"> <frame name="top" src="top.php" scrolling="auto" noresize> <frame name="main" src="main.php" scrolling="auto" noresize> </frameset> </html> 웹사이트의메인페이지 html 소스 main.php <html> <head> <meta HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=utf-8"> <title> :: PHP 프로그래밍입문에오신것을환영합니다 ~~ :: </title> <link rel="stylesheet" href="style.css" type="text/css"> </head> <body leftmargin="0" topmargin="0" marginwidth="0" marginheight="0"> <table width="776" align="center" cellspacing="0" cellpadding="0" border="0"> <tr height=150><td></td></tr> <tr align=center> <td> 메인화면입니다. </td> </tr> </table> </body> </html> 홈페이지메인화면상단메뉴 HTML, PHP 소스 top.php <?session_start();?> <html> <head> <meta HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=utf-8"> <title>:: PHP 프로그래밍입문에오신것을환영합니다 ~~ ::</title> <link rel="stylesheet" href="style.css" type="text/css"> </head> <body leftmargin="0" topmargin="0" marginwidth="0" marginheight="0"> <table width="776" align="center" cellspacing="0" cellpadding="0" border="0"> 13
<tr> <td> <table width=776 cellspacing="0" cellpadding="0" border="0"> <!--상단제목그림--> <tr> <td colspan="10"> <img border="0" src="img/title.gif" width="776" height="146"></td> </tr> <tr> <td height="8" colspan="10"> <img border="0" src="img/blank.gif" width="1" height="8"></td> </tr> <!--메뉴시작--> <TR> <TD> <a href="main.php" target="main"> <img SRC="img/menu_01.gif" WIDTH=104 HEIGHT=47 border=0 ALT=""></a></TD> <? if (!$userid) { echo " <TD> <a href='login/login_form.html' target='main'> <img SRC='img/menu_02.gif' WIDTH=138 HEIGHT=47 border=0 ALT=''></a></TD> "; else { echo " <TD> <a href='login/logoff.php' target='main'> <img SRC='img/menu_10.gif' WIDTH=138 HEIGHT=47 border=0 ALT=''></a></TD> "; if (!$userid) { echo " <TD> <a href='login/member_form.html' target='main'> <img SRC='img/menu_03.gif' WIDTH=214 HEIGHT=47 border=0 ALT=''></a></TD> "; 14
else { echo " <TD> <a href='login/modify_memberinfo.php' target='main'> <img SRC='img/menu_03.gif' WIDTH=214 HEIGHT=47 border=0 ALT=''></a></TD> ";?> <TD> <a href="course/co_in.html" target="main"> <img SRC="img/menu_04.gif" WIDTH=162 HEIGHT=47 border=0 ALT=""></a></TD> <TD> <a href="score/sc_in.html" target="main"> <img SRC="img/menu_05.gif" WIDTH=158 HEIGHT=47 border=0 ALT=""></a></TD> </TR> </table> </td> </tr> </table> <!--메뉴끝--> </body> </html> 데이터베이스와연결하는 PHP 소스 dbconn <??> $connect = oci_connect('dis_db', 'Q1w2e3r4', 'ORCL','AL32UTF8'); 로그인페이지 PHP 소스 login.php <meta http-equiv="content-type" content="text/html; charset=utf-8" /> <?session_start(); // 이전화면에서이름이입력되지않았으면 " 이름을입력하세요 " // 메시지출력 if(!$id) { echo(" <script> window.alert(' 아이디를입력하세요.') history.go(-1) </script> "); exit; 15
if(!$passwd) { echo(" <script> window.alert(' 비밀번호를입력하세요.') history.go(-1) </script> "); exit; function strfilter($str){ $str = preg_replace("/[\+%\\;\^~ \!\?\*$#\[\]\{\]/i", "", $str); $str = preg_replace("/</", "<", $str); $str = preg_replace("/>/", ">", $str); $str = preg_replace("/'/i", "a", $str); $str = preg_replace("/`/i", "b", $str); $str = preg_replace('/"/', "c", $str); $str = preg_replace("/--/i", "e", $str); /* $str = preg_replace('/([\x00-\x08\x0b-\x0c\x0e-\x19])/', '',$str); $str = preg_replace("/union[^\x21-\x7e]/i", "union ", $str); $str = preg_replace("/select[^\x21-\x7e]/i", "select ", $str); $str = preg_replace("/insert[^\x22-\x7e]/i", "insert ", $str); $str = preg_replace("/drop[^\x21-\x7e]/i", "drop ", $str); $str = preg_replace("/update[^\x21-\x7e]/i", "update ", $str); $str = preg_replace("/and[^\x21-\x7e]/i", "and ", $str); $str = preg_replace("/or[^\x21-\x7e]/i", "or ", $str); $str = preg_replace("/if[^\x21-\x7e]/i", "if ", $str); $str = preg_replace("/[^\x21-\x7e]union/i", " union", $str); $str = preg_replace("/[^\x21-\x7e]select/i", " select", $str); $str = preg_replace("/[^\x21-\x7e]insert/i", " insert", $str); $str = preg_replace("/[^\x21-\x7e]drop/i", " drop", $str); $str = preg_replace("/[^\x21-\x7e]update/i", " update", $str); $str = preg_replace("/[^\x21-\x7e]and/i", " and", $str); $str = preg_replace("/[^\x21-\x7e]or/i", " or", $str); $str = preg_replace("/[^\x21-\x7e]if/i", " if", $str); */ $str = preg_replace("/&/", "&", $str); $str = preg_replace("/&/", "&", $str); $str = preg_replace("/ /", " ", $str); $str = preg_replace("/</", "<", $str); $str = preg_replace("/>/", ">", $str); $str = preg_replace("/'/", "'", $str); $str = preg_replace("/`/", "`", $str); //$str = preg_replace("/"/", """, $str); $str = preg_replace("/_/", "_", $str); 16
return $str; $id=strfilter($id); $passwd=strfilter($passwd); include "../dbconn.php"; $sql = "select * from member where id='$id'"; echo $id; $result = oci_parse($connect, $sql); oci_execute($result); /* if(!@oci_execute($result)) error("sql구문에러 "); exit; */ $match = oci_fetch_all($result, $num_rows, 0, -1, OCI_FETCHSTATEMENT_BY_ROW); if($match==0) { echo(" <script> window.alert(' 등록되지않은아이디입니다.') history.go(-1) </script> "); else { $sql2 = "select * from member where id='$id' and passwd='$passwd'"; $result2 = oci_parse($connect, $sql2); oci_execute($result2); $match2 = oci_fetch_all($result2, $num_rows2, 0, -1, OCI_FETCHSTATEMENT_BY_ROW); { echo $match2; if($match2==0) echo(" "); <script> window.alert(' 비밀번호가틀립니다.') history.go(-1) </script> else exit; 17
{ $sql3 = "select id from member where id='$id'"; $result3 = oci_parse($connect, $sql3); oci_execute($result3); $row1 = oci_fetch($result3); $sql4 = "select name from member where id='$id'"; $result4 = oci_parse($connect, $sql4); oci_execute($result4); $row2 = oci_fetch($result4); $userid = $row1; $username = $row2; session_register(userid); session_register(username); echo(" "); <script> top.location.href = '../index.php'; </script>?> ID를체크하는 PHP 소스 check_id.php <meta http-equiv="content-type" content="text/html; charset=utf-8" /> <?php if(!$id) { echo(" 아이디를입력하세요."); else { include "../dbconn.php"; $sql = "select * from member where id='$id'"; $result = oci_parse($connect, $sql); oci_execute($result); $rows = oci_fetch_all($result, $num_rows, 0, -1, OCI_FETCHSTATEMENT_BY_ROW); 18
?> if ($rows>0) { echo " 아이디가중복됩니다.<br>"; echo " 다른아이디를사용하세요.<br>"; else { echo " 사용가능한아이디입니다."; oci_close($connect); 로그인페이지 HTML 소스 login_form.html <html> <head> <meta HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=utf-8"> </head> <body> <link rel="stylesheet" href="../style.css" type="text/css"> <form method=post action=login.php> <table align=center> <tr><td><img src="img/login.gif"></td></tr> <tr height=1 bgcolor=#5ab2c8><td></td></tr> <tr><td></td></tr> <tr><td><img src="img/star.gif"> 아이디 : <input type="text" name="id" size="20" maxlength="20"></td> </tr> <tr> <td><img src="img/star.gif"> 비밀번호 : <input type="password" name="passwd" size="20" maxlength="20"> </td> </tr> <tr height=1 bgcolor=#5ab2c8><td></td> </tr> <tr><td></td></tr> <tr> <td align=right> <input type=image src="img/login_on.gif" border=0> </a> <a href="member_form.html"><img src="img/member.gif" border=0></a> </td> </tr> </table> </form> </body> </html> 19
6. 참고문헌 [1] 황성운, 데이터베이스보안취약사이트공격및대응방안, 보안공학연구논문지 2011. 04 [2] KISA, 자동화된 SQL Injection 공격을통한악성코드대량삽입수법분석 한국정보보호진흥원 2008. 12 [3] 문성기, PHP입문에서최적화된 DB 연동까지 PHP, MySQL & Oracle 다올미디어 [4] 조은백, 데이터베이스보안, 생능출판사 [5] 김태근, 업그레이드된쿼리로만나는오라클 SQL & PL/SQL, 프리렉 [6] http://www.phpschool.com/ PHP school [7] http://www.kisa.or.kr/ 한국인터넷진흥원 20
7. 발표 PPT 21
22
23
24