정통부 “상위 1천개 사이트 웹개발자 대상 체계적 교육 계획”
지난주 보안뉴스는 ‘웹개발자, 보안의식 결여-관행적 개발 문제’라는 제하의 기사를 통해 웹개발자들이 웹개발시 관리자 페이지에 대한 접근통제 방법에는 어떤 것들이 있는지에 대한 문제를 KISA 자료를 통해 되짚어 본 바 있다.
몇몇 개발자들은 대부분 알고 있는 정보라는 댓글이 올라오기도 했지만 이러한 기본적인 방법도 등한시 하는 개발자들이 있어 지속적인 정보를 제공하고자 한다.
정보통신부 정보화기획실 정보통신기반보호대응팀 김동철 팀장은 31일 “지속적으로 이루어 지고 있는 웹 해킹에 대한 대비의 일환으로 개발자 교육을 철저히 할 방침”이라고 밝히고 “국내에서 방문자가 많은 사이트 1천개를 대상으로 해당 웹사이트를 개발한 개발자들에게 올해 안에 체계적인 웹개발 교육계획을 세우고 있는 중”이라고 강조했다. 물론 교육은 웹개발시 보안에 대한 의식교육과 웹개발 보안가이드라인을 철저히 교육하겠다는 의지로 보여진다.
또한 김 팀장은 “웹개발시 보안구축을 철저히 하지 않으면 추후 보완할 때는 더 많은 비용과 어려움이 있기 때문에 개발자들의 보안가이드라인 준수가 무엇보다 중요하다”고 강조했다.
이하는 지난주에 이어 KISA 자료를 통해 웹개발자들이 반드시 알고 적용해야할 기본적인 보안가이드라인에 대한 내용이다. 참고하기 바란다.
<기획-1> 취약한 세션 관리 (Cookie Injection)
쿠키(Cookie)는 사용자 정보를 유지할 수 없는 HTTP(Hyper Text Transfer Protocol)의 단점을 해결할 수 있는 방법의 하나로서 각 서버는 쿠키를 사용하여 브라우저가 갖고 있는 정보를 참조할 수 있다. 이와 같이 클라이언트에서 동작되는 쿠키는 암호화 등의 문제를 비롯하여 그 구조상 클라이언트 측에서의 조작으로 인한 다양한 문제점을 가지고 있어, 많은 웹 프로그래밍 언어들에는 서버에 클라이언트의 정보를 저장하는 세션(Session)을 지원하고 있다.
적절히 보호되지 않은 쿠키를 사용하면 Cookie Injection등과 같은 쿠키 값 변조를 통하여 다른 사용자로의 위장 및 권한상승 등의 문제가 생길 수 있다. 또한 쿠키 및 세션은 Cookie Sniffing 및 이후에 이야기 될 악성스크립트실행(XSS)를 통한 Cookie Hijacking 등과 같은 쿠키 값 복사를 통해 현재 활성화된 사용자의 권한복제 위험성이 존재한다.
위협 사례
인증을 처리하는 스크립트가 입력 값에 대해 적절히 검사하지 않았을 때 공격자는 Cookie 값을 변조하여 인증과정을 통과할 수 있다.
취약성 판단
-사이트에 로그인 한 후, 웹 브라우저의 주소 창에 javascript :document. cookie;를 입력해서 내용을 확인한 후, 해당 세션 쿠키를 사용하는 웹 애플리케이션 소스 점검을 통해 불법 변조 탐지 루틴이 있는지 확인한다.
-모든 인증 자격 증명(credential)과 세션 구분자가 SSL을 이용하여 지속적으로 보호되고 있는지 확인한다.
-사이트의 인증 메커니즘을 다양한 측면에서 검토하여, 사용자의 자격 증명이 정적인 상태(예:디스크에 저장되어 있는 상태)나 전송 중(예:사용자 로그인이 일어나고 있는 순간)에 보호되는지를 살펴보아야 한다.
-인가된 사용자만이 사용자의 자격 증명을 변경할 수 있는지 점검하기 위해서는 사용자의 자격 증명을 변조할 수 있는 가능한 모든 메커니즘을 검사하여야만 한다.
-세션 관리 메커니즘을 점검할 때는 세션 구분자가 지속적으로 보호되고 있는지 그리고 세션 관리 메커니즘이 사고나 악의적인 공격의 발생 가능성을 감소시켜줄 수 있는지를 검사한다.
일반 대책
-전송 중의 자격 증명 보호
가장 효과적인 방법은 SSL과 같은 기술을 사용하여 로그인 트랜잭션 전체를 암호화하는 방법이다. 서버로 전송하기 이전에 클라이언트 단에서 패스워드를 해쉬하는 형태로 변경하는 단순한 방법으로는 다른 공격자가 실제 패스워드를 모르는 상태에서 해쉬된 정보를 가로채어 서버로 그대로 전송하는 일이 발생하는 경우 별다른 보안을 제공하지 못한다.
-Cookie대신 보안성이 강한 Server Side Session을 사용
Client Side Session 방식인 Cookie는 그 구조상 다양한 취약점에 노출될 수 있으므로 가능한 웹 서버에서 제공되는 Server Side Session을 사용하는 것이 바람직하다.
개발 언어별 대책
쿠키 저장 시 타인이 임의로 쿠키를 읽어 들일 수 없도록 도메인과 경로 지정에 유의해야 하며, 서버 측에서 유효성 여부를 확인할 수 있는 대책을 강구한다. 예를 들어 브라우저에 저장된 쿠키에만 의존하는 쿠키방식보다는 서버 측에 일부 정보를 저장하여 상호 대조할 수 있는 세션(Session) 방식으로 대체하고, 세션방식의 경우도 서버 측에 사용자의 IP정보 등을 함께 저장하여 유효성 여부를 확인하는 방식을 권한다.
Session 방식은 접속자 별로 세션을 생성하여 사용자의 정보를 각각 저장할 수 있는 오브젝트로써 페이지의 접근을 허가하거나 금지할 때 또는 사용자별로 정보를 저장할 때 많이 사용된다. 클라이언트의 자원을 사용하는 쿠키와는 달리 세션은 서버 쪽의 자원을 차지하고 있으므로 보안을 고려하여 세션방식을 채택하는 것이 바람직하다.
■ASP
<취약한 프로그래밍 예>
'login_ok.asp 사용자 인증 처리를 하는 스크립트
<%
' form 에서 사용자 id와 사용자 password를 아래 변수로 전달
If myfunc_userauth(userid, userpw) <> 1 Then ' DB 에서 사용자 인증을 처리하는 부분
Response.write "인증 실패"
Else
'인증에 성공한 경우 처리 해야 되는 부분
Response.Cookies("logged_in") = 1
' 인증에 성공했을경우 logged_in 에 1의 값을 셋팅
Response.Cookies("userid") = userid
End If
...
%>
user_menu.asp' 사용자 검증이 필요한 페이지
<%
IF Request.Cookies("logged_in") = 1 Then
Response.write "허가된 사용자 입니다."
Else
Response.write "허가되지 않은 사용자 입니다."
End If
%>
<안전한 프로그래밍 예>
‘login_ok.asp 사용자 인증 처리를 하는 스크립트
<%
' form 에서 사용자 id와 사용자 password를 아래 변수로 전달
If myfunc_userauth(userid, userpw) <> 1 Then ' DB 에서 사용자 인증을 처리하는 부분
Response.write "인증 실패"
Else
'인증에 성공한 경우 처리 해야 되는 부분
If Session("logged_in") <> 1 Then
Session("logged_in") = 1'인증에 성공했을경우 logged_in 에 1의 값을 셋팅
Session("userid") = userid
Session("user_ip") = Request.Servervariables("REMOTE_ADDR")
End If
End If
...
%>
‘user_menu.asp 사용자 검증이 필요한 페이지
<%
IF Session("user_ip) = Request.Servervariables("REMOTE_ADDR") AND Session("logged_in") = 1 Then
'인증에 성공한 IP와 사용자 IP를 비교, 인증 여부 비교
'...
Else
Response.write "허가되지 않은 사용자 입니다."
End If
%>
■PHP
<취약한 프로그래밍 예>
//login_ok.php// 사용자 인증 처리를 하는 스크립트
<?PHP
// form 에서 사용자 id와 사용자 password를 아래 변수로 전달
if(!myfunc_userauth($userid,$userpw)) //DB 에서 사용자 인증을 처리하는 부분
print "인증 실패";
exit;//인증 실패시 종료
//인증에 성공한 경우 처리 해야 되는 부분
setcookie("logged_in", "1");//인증에 성공했을경우 logged_in 에 1의 값을 셋팅
setcookie("userid", $userid);
...
?>
//user_menu.php// 사용자 검증이 필요한 페이지
<?PHP
if($_COOKIE["logged_in"] == 1)
echo "인증 성공: " . $_COOKIE["userid"];
?>
<안전한 프로그래밍 예>
//login_ok.php// 사용자 인증 처리를 하는 스크립트
<?PHP
@session_start(); //세션 데이터를 초기화
// form 에서 사용자 id와 사용자 password를 아래 변수로 전달
if(!myfunc_userauth($userid,$userpw)) //DB 에서 사용자 인증을 처리하는 부분
print "인증 실패";
exit;//인증 실패시 종료
//인증에 성공한 경우 처리 해야 되는 부분
if (!session_is_registered("logged_in"))
$logged_in = 1;//인증에 성공했을경우 logged_in 에 1의 값을 셋팅
$user_ip = $_SERVER["REMOTE_ADDR"];
session_register("logged_in");//인증 결과 저장
session_register("userid");//사용자 ID를 저장
session_register("user_ip");//사용자 IP를 저장
...
?>
//user_menu.php// 사용자 검증이 필요한 페이지
<?PHP
session_start();
if(strcmp($_SESSION['user_ip'], $_SERVER['REMOTE_ADDR']) == 0 && session_is_registered('logged_in'))
//인증에 성공한 IP와 사용자 IP를 비교, 인증 여부 비교
//...
else
print "허가되지 않은 사용자 입니다.";
exit;
?>
■JSP
<취약한 프로그래밍 예>
<%@ page contentType="text/html;charset=euc-kr" %>
<%@ page import="java.util.*" %>
<%@ page import="java.sql.* " %>
//login_ok.jsp// 사용자 로그인 처리를 하는 스크립트
<%
// form 에서 사용자 id와 사용자 password를 아래 변수로 전달
if(!myfunc_userauth(userid, userpw)) //DB 에서 사용자 인증을 처리하는 부분
out.println "인증 실패";
else
//인증에 성공한 경우 처리 해야 되는 부분
Cookie cookie1 = new Cookie("logged_in", "1");
response.addCookie(cookie1);//인증에 성공했을경우 logged_in 에 1의 값을 셋팅
Cookie cookie2 = new Cookie("userid", userid);
response.addCookie(cookie2);
...
%>
//user_menu.jsp// 사용자 검증이 필요한 페이지
<%
Cookie[] cookies = request.getCookies();
for(int i=0; i< cookies.length; i++)
Cookie thisCookie = cookie[i];
if(thisCookie.getName.equals("logged_in"))
String logged_in = thisCookie.getValue();
if(thisCookie.getName.equals("userid"))
String userid = thisCookie.getValue();
if(logged_in.equals("1"))
out.println("인증 성공: " + userid);
%>
<안전한 프로그래밍 예>
<%@ page contentType="text/html;charset=euc-kr" %>
<%@ page import="java.util.*" %>
<%@ page import="java.sql.* " %>
//login_ok.jsp// 사용자 로그인 처리를 하는 스크립트
<%
//HttpSession session = request.getSession(true);
// form 에서 사용자 id와 사용자 password를 아래 변수로 전달
if(!myfunc_userauth(userid, userpw)) //DB 에서 사용자 인증을 처리하는 부분
out.println "인증 실패";
else
//인증에 성공한 경우 처리 해야 되는 부분
session.putValue('logged_in',"1");
session.putValue('userid',userid);
session.putValue('user_ip',request.getRemoteAddr());
...
%>
//user_menu.jsp// 사용자 검증이 필요한 페이지
<%
//HttpSession session = request.getSession(true);
String user_ip = session.getValue("user_ip");
if(user_ip.equals(request.getRemoteAddr()) && logged_in.equals("1"))
//인증에 성공한 IP와 사용자 IP를 비교, 인증 여부 비교
//...
else
out.println "허가되지 않은 사용자 처리.";
%>
[길민권 기자(boannews@infothe.com)]
<저작권자: 보안뉴스(www.boannews.com). 무단전재-재배포금지.>