보안 공부/소스코드 보안약점 진단

소스코드 보안약점 진단 - 크로스사이트 스크립트

H.J.World 2021. 11. 28. 19:49
728x90
반응형

소스코드 보안약점 진단 // 소프트웨어 보안약점 진단 // SW 보안약점 진단과 같이 다양한 이름으로 불리는 진단 과업 중 하나이다.

SW개발보안은 해킹 등 사이버공격의 원인인 보안약점을 SW개발단계에서 사전에 제거하고 SW 개발 생명주기의 각 단계별로 수행하는 일련의 보안활동을 통하여 안전한 SW를 개발·운영하기 위한 목적으로 적용하는 개발체계이다.

해당 내용은 KISA에서 발간하는 취약점 진단 가이드 항목을 기준으로 작성한다.

 

3. 크로스사이트 스트립트

가. 개요

웹 페이지에 악의적인 스크립트를 포함시켜 사용자 측에서 실행되게 유도할 수 있다. 예를 들어, 검증 되지 않은 외부 입력이 동적 웹페이지 생성에 사용될 경우, 전송된 동적 웹페이지를 열람하는 접속 자의 권한으로 부적절한 스크립트가 수행되어 정보유출 등의 공격을 유발할 수 있다.

나. 보안대책

외부입력값에 스크립트가 삽입되지 못하도록 문자변환 함수 또는 메서드를 사용하여 < > & “ 등을 < > & " 로 치환한다. HTML태그를 허용하는 게시판에서는 허용되는 HTML 태그들을 화이트리스트로 만들어 해당 태그만 지원하도록 한다.

 

다. 코드예제

크로스사이트 스크립트(XSS)는 크게 3가지 공격 방법이 존재한다. Reflected XSS 공격은 검색 결과, 에러 메시지 등을 통해 서버가 외부에서 입력받은 악성 스크립트가 포함된 URL 파라미터 값을 사용자 브라우저에서 응답할 때 발생한다. 공격 스크립트가 삽입된 URL을 사용자가 쉽게 확인할 수 없도록 변형하여, 이메일, 메신저, 파일등을 통해 실행을 유도하는 공격이다.

Stored XSS 공격은 웹 사이트의 게시판, 코멘트 필드, 사용자 프로필 등의 입력 form을 통해 악성 스크립트를 삽입하여 DB에 저장되면, 사용자가 사이트를 방문하여 저장되어 있는 페이지에 정보를 요청할 때, 서버는 악성 스크립트를 사용자에게 전달하여 사용자 브라우저에서 스크립트가 실행 되면서 공격한다.

DOM기반 XSS 공격은 외부에서 입력받은 악성 스크립트가 포한된 URL 파라미터 값이 서버를 거치지 않고, DOM 생성의 일부로 실행되면서 공격한다. Reflected XSS 및 Stored XSS 공격은 서버 애플리케이션 취약점으로 인해, 응답 페이지에 악성 스크립트가 포함되어 브라우저로 전달되면서 공격하는 것인 반면, DOM기반 XSS는 서버와 관계없이 발생하는 것이 차이점이다.

 

- 안전하지 않은 코드의 예 JAVA -

1: <% String keyword = request.getParameter("keyword"); %>
2: //외부 입력값에 대하여 검증 없이 화면에 출력될 경우 공격스크립트가 포함된 URL을 생성 할 수 있어 안전하지 않다.(Reflected XSS)
3: 검색어 : <%=keyword%>

외부 입력값 파라미터나 게시판등의 form에 의해 서버의 처리 결과를 사용자 화면에 출력하는 경우, 입력값에 대해서 문자열 치환 함수를 이용하여 스크립트 문자열을 제거하거나, JSTL을 이용하여 출력하거나, 잘 만들어진 외부 XSS 방지 라이브러리를 활용하는 것이 안전하다.

 

- 안전하지 않은 코드의 예 JAVA -

1: //게시판 등의 입력form을 통해 외부값이 DB에 저장되고, 이를 검증 없이 화면에 출력될 경우
공격스크립트가 실행되어 안전하지 않다.(Stored XSS)
2: 검색결과 : ${m.content}
3: <script type=“text/javascript”>
4: //외부 입력값에 대하여 검증 없이 브라우저에서 실행되는 경우 서버를 거치지 않는 공격스크립트가
포함된 URL을 생성 할 수 있어 안전하지 않다. (DOM 기반 XSS)
5: document.write(“keyword:” + <%=keyword%>);
6: </script>

 

- 안전한 코드의 예 JAVA -

크로스사이트 스크립트의 경우 동작 상황에 따라 동일한 조치방법을 사용하면, 크로스사이트 스크립트 방지는 되더라도 원하는 동작이 정상적으로 되지 않을 수 있기 때문에, 잘 만들어진 외부 XSS방지 라이브러리를 이용하여 각 동작 상황에 따라 적절하게 사용하는 것을 권장한다.

1: <% String keyword = request.getParameter("keyword"); %>
2: // 방법1. 입력값에 대하여 스크립트 공격가능성이 있는 문자열을 치환한다.
3: keyword = keyword.replaceAll("&", "&amp;");
4: keyword = keyword.replaceAll("<", "&lt;");
5: keyword = keyword.replaceAll(">", "&gt;");
6: keyword = keyword.replaceAll("\"", "&quot;");
7: keyword = keyword.replaceAll("'", "&#x27;");
8: keyword = keyword.replaceAll("/"", "&#x2F;");
9: keyword = keyword.replaceAll("(", "&#x28;");
10: keyword = keyword.replaceAll(")", "&#x29;");
11: 검색어 : <%=keyword%>
12: //방법2. JSP에서 출력값에 JSTL c:out 을 사용하여 처리한다.
13: <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
14: <%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>
15: 검색결과 : <c:out value="${m.content}"/>
16: <script type="text/javascript">
17: //방법3. 잘 만들어진 외부 라이브러리를 활용(NAVER Lucy-XSS-Filter, OWASP ESAPI, OWASP Java-Encoder-Project)
18:document.write("keyword:“ + <%=Encoder.encodeForJS(Encoder.encodeForHTML(keyword))%>);
19: </script>

 

- 안전하지 않은 코드의 예  C -

아래 C 코드 예제는 외부 입력값으로 사용자로부터 받은 입력값을 검증 없이 바로 cgi에 출력하는 화면이다.

1: int XSS(int argc, char* argv[]) {
2: unsigned int i = 0;
3: char data[1024];
4: …
5: // cgiFromString으로 받아온 사용자 입력값이 검증 없이 화면에 출력됩니다.
6: cgiFromString(“user input”, data, sizeof(data));
7: fprintf(cgiOut, “Print user input = %s<br/>”, data);
8: fprintf(cgiOut, “</body></html>\n”);
9: return 0;13:
10: }

 

- 안전한 코드의 예 C -

cgi에 출력하기 전에 사용자 입력값을 검증하여야 한다.

1: cgiFromString(“user input”, data, sizeof(data));
2: // data에 위험한 문자열을 검사하는 코드를 추가한다.
3: if(strchr(p, ‘<’)) return;
4: if(strchr(p, ‘>’)) return;
5: …
6: fprintf(cgiOut, “Print user input = %s<br/>”, data);
7: fprintf(cgiOut, “</body></html>\n”);

 

라. 진단방법

웹 페이지로 출력하는 변수값이 존재하는지 확인하고(①), 해당 변수값이 외부 입력값 또는 사용자 입력 데이터 폼에 의해 저장된 데이터 베이스 값인지 확인한(②) 후 변수값이 적절하게 필터를 거치 는지 확인한다. 적절한 필터를 거친 후 출력되는 경우나 프레임워크 등을 사용하여 자체적인 검증 기능이 존재하면 안전하지만 그 외에는 취약하다.

- 일반적인 진단의 예 -

1: <%@page contentType=”text/html” pageEncoding=”UTF-8”%>
2: <html>
3: <head>
4: <meta http-equiv=”Content-Type” content=”text/html; charset=UTF-8”>
5: </head>
6: <body>
7: <h1>XSS Sample</h1>
8: <%
9: // 외부로부터 이름을 받음
10: String name = request.getParameter(“name”);------------------②
11: String title = null;
12: …
13: rs = pstmt.executeQuery(query);
14: while(rs.next()){
15: title = rs.getString(“title”))----------------------------------②
16: …}
17: %>
18:
19: <!-- 외부로부터 받은 name이 적절한 필터없이 그대로 출력 -->
20: <p>NAME:<%=name%></p>--------------------------------①
21: <p>TITLE:<%=title%></p>----------------------------------①
22: </body>
23: </html>

 

<크로스사이트 스크립트 : Reflected> 다음의 예제에서 @ModelAttribute(“apApsCommonCodeVO”) ApApsCommonCodeVO apApsCommonCodeVO 어노테이션에 따라 apsApsCommonCodeVO는 외부 입력값이다. 20라인에서 외부 입력값의 target값을 저장하고 26번째 라인에서 외부 입력값을 response의 output stream에 출력하고 있다. 그 과정에서 입력값에 대한 필터링이 없으므로 취약한 것으로 판정한다.

16: @RequestMapping(“/aps/common/makeCommonCode.do”)
17: public void makeCommonCode(HttpSession session,
@ModelAttribute(“apApsCom- monCodeVO”)
18: ApApsCommonCodeVO apApsCommonCodeVO, HttpServletRequest request
,HttpServletResponse
19: response, ModelMap model) throws Exception {
20: String target = apApsCommonCodeVO.getTarget();
21: List resultList = apApsExcelService.selectCommonCode(apApsCommonCodeVO);
22: JSONArray jsonArray = new JSONArray();
23: jsonArray = JSONArray.fromObject(resultList);
24: response.setContentType(“text/xml;charset=utf-8”);
25: PrintWriter printWriter = response.getWriter();
26: printWriter.print(target);
27: printWriter.print(jsonArray.toString());
28: printWriter.flush();
29: printWriter.close();
30: }

 

- 오탐코드의 예 -

다음의 예제에서는 외부 입력값에 스크립트 공격을 시도하더라도, 해당 스크립트 공격이 URL로 포함 되어 이동하기 때문에, 공격이 수행 되지 않아 안전한 것으로 판정한다.

1: <%=respose.sendRedirect(requst.getParameter(“url”))%>

 

다음의 예제에서는 외부 입력값에 스크립트 공격을 시도하더라도, 해당 스크립트 공격이 URL로 포함 되어 이동하기 때문에, 공격이 수행 되지 않아 안전한 것으로 판정한다.

1: <script language=”javascript”>
2: location.href(‘<%=request.getParameter(“url”)%>’);

 

다음의 예제에서와 같이 JSTL의 을 이용하여 외부 값을 출력할 때는 안전한 것으로 판정한다.

1: <%@ taglib prefix=”c” url=”http://java.sun.com/jsp/jstl/core”%>
2: <%@ taglib uri=”http://java.sun.com/jsp/jstl/functions” prefix=”fn”%>
3: …
4: <c:out value=”${param.name}”/>

 

다음의 예제에서와 같이 JSTL의 escapeXml을 이용하여 외부 값을 출력할 때는 안전한 것으로 판정 한다.

1: <%@ taglib prefix=”c” url=”http://java.sun.com/jsp/jstl/core”%>
2: <%@ taglib uri=”http://java.sun.com/jsp/jstl/functions” prefix=”fn”%>
3: …
4: ${fn:escapeXml(param.name)}

 

다음의 예제와 같이 subString을 이용하여 글자 수가 짧은 글자 수 이하로 제한된 경우에는 안전한 것으로 판정한다.

1: <%
2: // 외부로 부터 입력을 받는다.
3: String date = request.getParameter(“date”);
4: String year = date.substring(0, 4);
5: String month = date.substring(4, 6);
6: String day = date.substring(6, 8);
7: out.println(year + “년” + month + “월” + day + “일” );
8: %>

 

다음의 예제와 같이 웹페이지가 아닌 System 콘솔에 출력하는 경우에는 안전한 것으로 판정한다.

1: //파라메터 설정
2: String exam_tgt_se = request.getParameter(“exam_tgt_se”);
3: String mw_take_no = request.getParameter(“mw_take_no”);
4: String apv_perm_reg_mgt_no = request.getParameter(“apv_perm_reg_mgt_no”);
5: String mw_afr_no = request.getParameter(“mw_afr_no”);
6:
7: System.out.println(“exam_tgt_se : “ + exam_tgt_se);
8: System.out.println(“mw_take_no : “ + mw_take_no);
9: System.out.println(“apv_perm_reg_mgt_no : “ + apv_perm_reg_mgt_no);

 

728x90
반응형