블로그 이미지
자유로운설탕

calendar

1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31

Notice

'자바스크립트'에 해당되는 글 1

  1. 2019.03.02 구글로 공부하는 보안 - 7교시 (스크립트 문제)2
2019. 3. 2. 22:32 보안

  사실 스크립트 문제는 명시적으로 클라이언트나 서버에서 돌아가는 모든 스크립트가 문제이긴 하지만, 여기서는 클라이언트 쪽(정확히는 브라우저)의 자바스크립트 만을 대상으로 얘기하려 한다.

 

  일반적으로 자바스크립트 문제로 얘기되어 지는 XSS(Cross Site Script)는 인젝션 문제와 기본적으로 유사한 형태라고 생각된다. 다만 브라우저와 밀접하게 움직이는 클라이언트 코드인 자바스크립트의 특성 때문에, 최악의 상황에는 브라우저를 뒤에서 조정한다고도 할 수 있는 비동기 루프까지 일으킬 수 있기 때문에 더 특별히 다뤄지는게 아닐까도 싶다. 여튼 상황에 따라 단발성이 아닌 살아 있는 것 같이 사용자를 계속 괴롭힐 수 있는(물론 브라우저를 끄기 전까지에 한정되 있지만) 특이한 요소이다.

 

 

 

[목차]

1. 보안을 바라보는 방법

2. 보안에서의 코드 읽기

3. 인젝션(Injection) 살펴보기 #1, #2

4. 암복호화

5. 클라이언트 코드

6. 업로드, 다운로드

7. 스크립트 문제

8. API

9. 설정, 패치, 하드닝

10. 설계 문제

11. 스캐너 vs 수동 테스트

12. 자동화 잡

13. 리버싱과 포렌식

14. 모니터링 문제

15. 악성코드

16. 보안과 데이터

 

 

 

1. 들어가면서

  자바스크립트 문제에 영향을 줄 수 있는 요소들은 브라우저, 프레임워크 특성, 요구사항 정도 일거 같다.

 

  먼저 브라우저는 워낙 다양한 브라우저가 존재하고, 해당 브라우저 마다 XSS 시도에 대해서 서로 다른 디폴트 대응을 하고 있다(자바스크립트를 이용해 웹 프로그램을 만들어 보면 브라우저 호환성이 얼마나 속을 썩이는 지를 알수 있다). 어떤 브라우저는 모든 XSS 공격 시도에 대해서 허용하고 있고, 어떤 브라우저는 특정의 XSS 공격 시도에 대해 명시적으로 방어해 주기도 한다. 다만 상황에 따라 특정 자바스크립트 코드의 실행이 XSS 시도인지 브라우저 쪽에서 판단하는 것은 참 애매한 일이다. 잘못하면 정상적인 자바 스크립트 코드의 실행을 막을 수도 있기 때문이다. 해당 부분은 관련된 코드를 직접 보면서 한번 생각해 보도록 하자.

 

 

  두 번째로 프레임워크 특성 부분이다. 보통 요즘의 프로그래밍 언어들은 웹프로그래밍을 위해 제공하는 웹프레임워크 들이 있다. 해당 프레임워크마다 권고하는 설계가 있는데, 해당 설계를 따라 프로그램을 만들었을때, 자동으로 어디까지 XSS 로 의심되는 시도가 막히게 되느냐에 대한 문제가 있다. 어떤 언어는 해당 부분을 특정한 라이브러를 제공해서 개발자에게 명시적으로 해당 라이브러리를 사용해 방어코드를 넣도록 하는 경우도 있을테고, 어떤 경우는 프레임워크 자체에서 모든 전달된 변수에 대해 해당 방어 매커니즘을 자동으로 반영하는 경우도 있을 것이다. 그런데 여기서도 마찬가지로 지나친 안전을 보장하게 되면 모든 코드를 XSS 위험요소가 있는 것같이 다루게 되어 기능의 유연성이 떨어지는 문제가 생기게 될수도 있다. 이 부분도 뒤에서 플라스크 코드를 통해서 살펴보도록 하자. 

 

 

  마지막으로 요구사항 측면도 많은 영향을 미칠 수 있다. 예를 들어 게시판을 하나 만든다고 생각해 보자. 해당 게시판의 컨텐츠의 구성요소가 텍스트 기반이냐, HTML 기반이냐, 자바스크립트도 포함하느냐에 따라서 XSS 방어의 구현 난이도는 많이 차이가 나게 된다.

 

  텍스트 기반인 경우는 XSS 로 악용이 될 수 있는 모든 이상한 문자들을 HTML 인코딩 또는 제거를 통해서 나름 안전하게 데이터를 필터링 할 수 있다. 하지만 블로그와 같은 HTML 코드의 허용이 필요해버리면, 어떤 HTML 태그나 요소는 허용하고, 어떤 것은 제한 시킬지에 대해서 정교한 정책의 설계와 보안 테스트가 되어야 한다. 게다가 해당 부분은 위에 얘기한 브라우저 별로 검토되어야 하며, 흔히 여러 환경에서의 호환 테스트가 그렇듯이 그렇게 될경우 테스트 케이스의 수는 컨트롤 하기 힘들 정도로 계속 증식되게 된다.

 

  아래 링크에서 자바스크립트 체크 회피를 시도하는 XSS 공격에 악용 될수 있는 HTML 태그나 이벤트, 쿠키, 스타일 태그 등의 예제를 보여주고 있다. 보기만 해도 머리가 아파지지 않나 싶다. 만약 요구사항에 관리자들에게 필요하다며 게시판 작성시 자바스크립트 기능도 넣어달라고 해버리면 머리는 더 아파진다. 이런 경우 자바스크립트 방어를 포기하고 사용자 권한 관리 쪽에 포커스를 두거나, 커스텀한 편집 마법사를 만들어서, 사용가능한 자바스크립트 코드 기능을 제한하면서, 사용자가 직접 자바스크립트 편집을 하지 않게 하고 실제 코드의 생성 및 수정 처리는 서버단에서 하게 하는 방법도 있을 듯 싶다(공수는 엄청 들겠지만 말이다).

 

[XSS Filter Evasion Check Sheet - OWASP]

https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet

 

 

  특정 사이트 들에서는 BBCODE 라는 게시판용 커스텀 스크립트 언어를 쓴다고도 하지만, 아무래도 일반성 부분에서의 HTML 호환성을 포기하는 대가가 있을 것은 같다. 그리고 아마 저런 새로운 스크립트가 유명해지고, 자바스크립트 만큼 기능성이 확장된다면 역시 복잡도에 기반한 악용을 하는 시도는 늘어날 것이기 때문에 현실에 완벽한 해답은 없어 보인다.

 

[BBCode - WIKI]

https://ko.wikipedia.org/wiki/BBCode

 

 

 

 

2. 자바스크립트 공격의 종류

  보통 웹을 찾아보면 자바스크립트 공격은 3가지 종류로 나눌 수 있게 되는데, Reflected(반사), Stored(저장), DOM Based(돔 기반) 정도로 나누어지고, 부록으로 CSRF(Cross Site Request Forgery) 같이 연관된 항목이 있다.

 

  개인적으로 Reflected 와 DOM Based XSS 는 비슷한 종류라고 보고 싶다. 왜냐하면 두 항목의 차이는 브라우저 외부에서 입력된 스크립트 조각이 프로그래밍 언어의 출력 명령(PHP의 echo 같은)을 통해 화면에 표시되느냐, HTML 코드내에 포함된(또는 외부 링크일수도 있지만) 기존 자바스크립트에 의해서 표시되느냐의 차이일 뿐이기 때문이다. 여튼 두 가지는 클라이언트 코드, 인젝션 시간에 얘기한 브라우저 파서가 외부에서 들어온 자바스크립트를 어떻게 처리하느냐의 측면이라고 보면 될 듯 싶다.

 

 Stored 의 경우는 보통 Reflected 보다 심각한 문제라고 보는데 해당 부분에는 2가지 측면이 있다. 첫 째는 보통 해당 데이터가 게시판의 게시글 같이 DB에 저장된 상태로 보관되어 게시글을 보는 불특정 다수에게 노출이 된다는 부분이다. 둘째는 사실 이 글에서 얘기하고 싶은 요점이기도 한데 XSS 를 적극적으로 막아주는 브라우저도 이 타입의 스크립트에 대해서는 쉽게 선뜻 악용의 여부를 판단하기 힘들어 소극적이라는 부분이다. 왜냐하면 이 타입은 사실 Stored 란 이름 때문에 저장된 타입이라는 관점에서 보는 일이 많지만, 프로그램의 관점에서 보면 프로그램 내부에서 생성한 자바스크립트를 브라우저가 막을수 있겠냐는 딜레마의 측면이 있기 때문이다.

 

  마지막으로 CSRF 는 이름만 보면 난해해 보이지만, Forgery 가 '위조'라는 의미므로 요청(Request)을 위조한다는 의미라서 보통 인증에 사용하는 쿠키를 보유한 사용자가 모르는 사이에 XSS 공격을 이용해서, 대상 쿠키를 만들어낸 사이트의 주요 URL을 호출해 원하는 기능을 실행(예를 들어 패스워드 변경, 주소지 변경)하게 하는 요소로서 사이트의 주요한 기능에 대해서 쿠키 이외의 제대로된 추가 인증을 하고 있느냐가 문제가 되는 부분이라서, 사실 XSS 와는 관계가 있으면서도 관계가 없는 부분이기도 하다.

 

  그럼 주요 요소인 Reflected 와 Stored 공격에 대해서 브라우저들이 어떻게 반응을 하고 있는지 코드와 함께 살펴 보도록 하자.

 

 

 

 

3. Reflected vs Stored 공격 살펴보기

  아무래도 옛날 웹 프로그램 언어들이 이런 보안 부분에 아무 관심이 없는 편이므로, 3교시에 봤던 ASP 코드로 관련 코드를 만들어 본다.

 

 

 

3.1 POC 코드 만들고 엣지브라우저 또는 IE 에서 실행해보기

   해당 코드는 아래와 같다. 간단한 코드로 하나의 페이지에서 폼 전송과, 받기가 동시에 이루어진다. 위 쪽으로 보면 strFromGet 이란 변수는 폼에서 전송된 fromGet 이름을 가진 사용자가 입력한 input 박스 내용을 받고, strFromDatabase 변수는 원래는 데이터베이스를 갔다와야 하지만, 어차피 쿼리 결과를 변수에 담는거니 저런 스크립트 코드 데이터가 데이터베이스 안에 저장되어 있었다고 가정하자(이 부분이 앞서 얘기한 프로그램 내부에서 생성한 데이터 측면이 된다). 하단을 보면 Response.Write(PHP 의 echo 와 동일하다고 보면 된다)를 이용해서 해당 두 변수를 웹 화면에 뿌려준다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<%
    ' 폼 값 받기
    strFromGet = request("fromGet")
    strFromDatabase = "<script>alert('stored xss run')</script>"
%>
 
<html>
   <head>
      <title>XSS 시연</title>
   </head>
 
<body>
     <form name = "Custom" method="get" action="show_xss.asp">
        <table width = 500>
            <tr>        
                <td> ID </td>
                <td> <INPUT maxlength="100" name="fromGet" size="50" type="text"> </td>
                <td> <input name=button type=submit value="ID 보내기"> </td>
            </tr>      
        </table>   
    </form>      
    
    <hr>
    <br><b>결과</b>
    <br><br>
        
<%
    ' 두 개의 변수 표시    
    Response.Write "Reflected XSS : " & strFromGet & "<br><br>"
    Response.Write "Stored XSS : " & strFromDatabase & "<br><br>"
%>
 
</body>
</html>
cs

[show_xss.asp]

 

  그럼 위의 코드를 c:\inetpub\wwwroot 에 ANSI 포맷으로 show_xss.asp 라고 저장한다. 이 후 우선 엣지브라우저를 열어 http://localhost/show_xss.asp 라고 입력한다(윈도우 10 기준이고, IE11 이나 엣지 브라우저 모두 같은 결과를 가져오니 아무거나 해도 된다).

 

 

  처음에 뜨는 Stored XSS Run 이라는 alert 은 일단 무시하고, ID 입력 창에 "<script>alert('reflected xss run')</script>" 라고 적은 후,  "ID 보내기" 버튼을 누른다.

 

 

 그렇게 되면 처음에 입력한 값을 웹 화면에 표시했기 때문에 페이지에서 자바스크립트가 실행되어 "reflected xss run" 이라는 alert 이 뜨고, 확인을 누르면 다시 "stored xss run" 이라는 alert 이 뜬다.

 

 

 

  F12 를 눌러 개발자 도구에서 페이지 소스를 보면 아래와 같이 2개의 alert 을 띄우는 스크립트 코드가 내부안에 들어가 있다.

 

 

 

3.2 크롬에서 실행해 보기 

  이번엔 IE 가 아닌 크롬에서 같은 페이지를 열어 시연해 보자. 현재 크롬 최신 버전은 72 버전이다. 크롬에서 실행해 보면 처음 "stored xss run" 얼랏이 뜨는건 마찬가지지만, 입력 박스에 스크립트를 넣어 전송하게 되면 아래와 같이 XSS_AUDITOR 에 의해 에러가 났다는 페이지가 뜨면서 실행이 막히게 된다.

 

 

  마우스 오른쪽 버튼을 클릭해 소스 보기를 하면 크롬이 페이지를 막은 부분의 스크립트가 표시된다. 크롬이 자체적으로 외부에서 들어온 악성 스크립트가 실행된다고 판단해서 실행을 막은 것이다.

 

 

 

3.3 브라우저 입장에서 생각해 보기.

  자 그럼 브라우저 입장에서 한번 해당 코드를 생각해 보자. 우선 위의 코드 실행 결과를 보고 어떤 브라우저가 더 안전하구나 생각하는 건 사실 좀 애매한 관점이다. 그것보다는 XSS 보안에 대해서 어떤 접근법을 취하느냐에 대해 살펴보는게 더 정확할 듯 하다.

 

 

  Reflected XSS 경우 브라우저는 해당 현상이 일어나고 있다는 것을 누구 보다 잘 알고 있다. 왜냐하면 해당 스크립트 코드 조각을 보낸 것이 바로 자기 자신이고(사용자가 입력한 값을 브라우저가 전달했으니), 응답 받은 코드에 보낸 코드 조각이 똑같이 있으므로(일단 브라우저는 요청된 결과를 소스로 받아서 이리저리 해석하니까 처음 받는건 스크립트가 포함된 HTML 소스이다), 자기가 보낸 코드가 반사(reflected) 되었다는 사실을 알수 있다(사실 다른 시간에 다시 얘기하겠지만 웹 취약점 스캐너의 주요 원리도 대충 이런 식이다).

 

  그래서 refelected XSS 의 경우는 엣지나 크롬 모두 잘 인지하고 있지만, 엣지는 해당 부분은 통과시켜주고, 크롬은 막고 있다.

 

  ※ 엣지 브라우저에 XSS 필터가 제거된 것은 2018년 7월 인거 같다. 자세한 것은 아래 글 같은 부분을 참고하면 될 듯 하다. Content Security Policy 로 보호가 대체 될수 있다고 얘기하고, 여기서도 XSS 필터가 reflected XSS 만 막을 수 있다는 부분을 얘기하고 있다.

[edge to remove xss auditor - scotthelme 사이트]

https://scotthelme.co.uk/edge-to-remove-xss-auditor/

 

 

  문제는 XSS 취약점 중 영향이 크다고 얘기되고, 실제로 영향이 큰 경우는 stroed XSS 라는 것이다. 위의 예제에서 봤듯이 두 브라우저 모두 해당 코드를 실행 시켜준 이유는, 이게 해킹이 된 내부 데이터로 부터 날라온건지, 원래 프로그램이 실행시키기를 원해 만든 정상적인 스크립트 인지를 판단하는 것이 종합적인 배경지식이 없다면 현실적으로 불가능 하다는 점이다(저 위의 Content Security Policy 라는 나름 이런 부분을 정의하여 접근을 구분하겠다는 것으로 보이긴 하지만, 모든 브라우저가 지원해야 되고, 페이지 마다 권한을 정의해야 되니 설계가 많이 복잡해지긴 할 듯 싶다.

 

  사실 그런 종합적인 배경지식은 페이지를 설계한 기획이나 개발자가 제일 잘 알고 있다. 그래서 어느 정도 브라우저에도 의존할 수도 있고, 해당 스크립트 코드가 들어간 링크를 누군가 명시적으로 클릭하기 전에는(물론 인지 못하고 클릭할 가능성이 높지만) 일어날 수 없는 reflected XSS 와는 달리 stored XSS 는 100% 의도적인 보안 설계로 풀어야 한다. 

 

 

 

 

4. flask 방어 코드 예제 보기

   XSS 필터를 이용한 예제도 괜찮겠지만, flask 코드에서 자동 방어해주는 부분을 한번 보도록 하자. 정확히 얘기하면 플라스크에서 사용하는 jinja2 템플릿에서 지원해준다고 보면 될것 같다. 역시 간단한 코드를 보도록 하겠다(flask 동작에 대해 잘 모르겠으면 파이썬 19교시를 보고 오자).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from flask import Flask, make_response, render_template
 
# flask 웹서버를 실행 합니다.
app = Flask(__name__)
 
@app.route("/xss", methods=['GET'])
def xss():
 
    xss_string = "<script>alert('flask reflected xss run')</script>"
    return render_template('xss.html', name = xss_string)
 
 
# 이 웹서버는 127.0.0.1 주소를 가지면 포트 5000번에 동작하며, 에러를 자세히 표시합니다 
if __name__ == "__main__":
    app.run(host='127.0.0.1',port=5000,debug=True)
cs

[flask_xss.py]

 

  위의 코드를 c:\python\code 에 UTF-8 포맷으로 flask_xss.py 이름으로 저장한다. 

 

 

  이후 템플릿 파일을 만들어 보자. 한줄짜리 간단한 html 이다.

1
<td>{{ name }}</td>
cs

 

  위의 코드를 c:\python\code\templates 폴더에 UTF-8 포맷으로 xss.html 로 저장한다. 이후 플라스크 웹서버를 실행 시킨다.

 

c:\Python\code>python flask_xss.py
 * Serving Flask app "flask_xss" (lazy loading)
 * Running on
http://127.0.0.1:5000/ (Press CTRL+C to quit)

 

  이후 엣지 브라우에서 http://localhost:5000/xss 주소를 열어본다. 아래와 같이 스크립트가 실행되지 않고, 일반 문자로 표시된다. html escape 처리가 된 것이다.

 

 

  실제 이스케이프 처리가 된 것을 보기위에 소스보기를 하면 아래와 같이 이스케이프 처리가 자동으로 된 것을 볼 수 있다. 

 

 

  그럼 여기서 이런 생각이 들 수 있다. "그런데 만약 자바스크립트를 flask 로직쪽에서 생성해서 동적으로 HTML 페이지 쪽에 랜더링하는 경우라면 어떻게 할까?" 이다. 이 경우 해당 필터를 제거하는 방법은 간단하다. 아래와 같이 xss.html 파일에서 받은 변수의 뒤에 "|safe" 문장을 추가하여 저장하면 된다. 

1
<td>{{ name|safe }}</td>
cs

 

 

  이후 다시 같은 url 을 실행하면, 자바스크립트가 이스케이프 처리가 안되고 그대로 실행되는 것을 볼 수 있다. 다만 이 경우는 무조건 좋아할 건 아니고, 스크립트를 무효화 하는 기능이 해제된거니, 본인이 직접 해당 넘어온 코드의 안전성을 보장하는 부분을 고민해야 하는 문제가 생긴다. 세상에 완전한 공짜 점심은 없다. 

 

 

 

 

5. 응용편 - 비동기 호출과의 만남.

  추가로 공격의 XSS 의 최악의 상황을 묘사해 보기 위해서, 극단적이긴 하지만 비동기 코드에서 일어나는 상황을 한번 만들어 보자.

 

 

  우선 flask 코드는 아래와 같다. 보게 되면 2개의 라우팅 경로가 있는데, /xss_ajax 는 기본 인젝션 페이지로 stored XSS 가 전달되고 있고(reflected 보다 POC 코드 만들기가 편해서 이렇게 했다^^;), xss_ajax.html 을 랜더링 패이지로 쓴다.

 

  밑의 /getData 경로는 웹페이지에서 AJAX 호출을 하는 경로인데, 내용을 보면 단순하게 id 값 인자를 받아서 "hello, 받은 id" 이라는 값을 조합해서 반환한다. 랜더링 때 인젝션 되는 스크립트 코드를 보면 setInterval 함수를 이용해서 매 3초(3000)마다 GetData() 함수를 반복 호출하는 코드이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
from flask import Flask, make_response, render_template, request
 
# flask 웹서버를 실행 합니다.
app = Flask(__name__)
 
@app.route("/xss_ajax", methods=['GET'])
def xss_ajax():
    
    xss_string = """<script>
                       setInterval(function() {
                          GetData();
                       }, 3000);
                    </script>"""
    return render_template('xss_ajax.html', name = xss_string)
    
    
 
@app.route("/getData", methods=['GET'])
def getData():
    your_id = request.args.get('id')
    return "hello, " + str(your_id)
 
 
# 이 웹서버는 127.0.0.1 주소를 가지면 포트 5000번에 동작하며, 에러를 자세히 표시합니다 
if __name__ == "__main__":
    app.run(host='127.0.0.1',port=5000,debug=True)
cs

 [flask_xss_ajax.py]

 

  일단 c:\python\code 폴더에 UTF-8 포맷으로 flask_xss_ajax.py 라고 저장을 한다.

 

 

 다음은 템플릿 소스이다(기본적인 AJAX 코드 동작을 모르는 경우는 클라이언트 코드 편을 보고 온다). 스크립트를 보면 단순하게 페이지가 로딩이 완료(document.ready)되는 순간에 GetData 함수를 호출 한다. GetData 함수는 my_id 인자에 tom 이라는 값을 넣어, flask 의 getData 라우팅 주소를 호출한다. 그리고 반환된 결과를 아래의 span 태그 안에 표시한다. 그 밑의 {{ name|safe }} 는 일부러 XSS 취약점을 일으키게 위해 HTML 인코딩을 제거했다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<html>
    <head>
        <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
        <title>AJAX XSS Sample</title>
    </head>
    <body>
        <script type="text/javascript">
 
            $(document).ready(function(){
                GetData();
            });
 
            function GetData(){
                var my_id = "tom"
                $.ajax({
                    url : '/getData',
                    type : 'GET',
                    data : {id: my_id},
        
                    success: function(res){
                        $("#spanName").html(res);
                    }
                });
            }
        </script>
                        
        <span id="spanName"></span>
        <td>{{ name|safe }}</td>
    </body>
</html>
cs

[xss_ajax.html]

 

  해당 소스를 c:\python\code\templates 폴더에 UTF-8 포맷으로 xss_ajax.html 이름으로 저장한다. 이후 페이지를 한번 실행해보자. XSS 공격만 없다면 최초 페이지를 로딩했을때, getData 경로를 한번 호출하는 ajax 코드를 이용해 결과를 받아, 페이지에 hello, tom 이라고 표시해주는 작업을 하게 되는 페이지이다. 이후 플라스크 웹서버를 실행 시킨다.

 

c:\Python\code>python flask_xss_ajax.py
 * Serving Flask app "flask_xss_ajax" (lazy loading)
 * Running on
http://127.0.0.1:5000/ (Press CTRL+C to quit)

 

 

  엣지나 크롬 브라우저에 http://localhost:5000/xss_ajax 경로를 실행 하면, 아래와 같이 평범한 페이지가 실행이 된다. 사용자 입장에서는 특별한 일이 없는 것으로 보인다.

 

 

  하지만 피들러를 띄운 상태에서 해당 페이지를 띄워보면 전혀 다른 상황이 되어 버린다. 아래와 같이 한번 호출 하고 말아야 했던 /getData 경로가 3초에 한번씩 무한으로 실행되고 있다(브라우저를 끄면 종료된다). 이론상 스크립트를 어떻게 작성하고 어떤 AJAX 기능을 호출하냐에 따라서 마음대로 할수 있다(이렇게 되면 SQL Injection 이 일어났을때 공격자가 마음대로 쿼리를 만들어 내는 거나, 업로드 취약점으로 웹쉘이 올라갔을때와 비슷한 느낌이 된다). 인증된 토큰이나 쿠키는 이용하지 않지만, 위의 예제를 CSRF 공격의 AJAX 버전이라고 봐도 될듯 하고 말이다.

 

 

  보안 교육 할때 브라우저를 마음대로 조정한다고 많이 시연되는 Beef 라는 툴도 결국은 이와 비슷한 원리로 잘 짜여진 ajax 스크립트가 기반일 것이다.

 

[beef project]

https://beefproject.com/

 

 

 

 

6. XSS 방어 설계

  그럼 우리가 생각했던 여러 포인트 들을 정리해서 XSS 이슈에 어떻게 접근해야 될지 한번 고민해 보자. 우선 XSS 의 패턴은 너무 많다. 그러므로 가장 바람직한 경우는 영향을 최소화 하기 위해서 스크립트나 HTML 을 표시하는 기능을 필요한 만큼만 최소화해서 구현 하는 것이다. 사용할 수 있는 태그 제한이 가능하다면 가능한 태그와 문법만 허용하는 화이트 박스 방식으로 체크하면 좋을 서이다.(이런 이상적인 경우는 드물긴 하겠지만...)

 

 

  reflected XSS 는 브라우저가 인지해서 어느정도 방어할 수는 있지만, 세상 사람들이 가진 브라우저는 워낙 종류와 버전이 다양하므로 브라우저만의 방어를 믿을 수는 없다. 앞에서 보듯이 IE 계열은 그대로 실행도 시켜주고 있다. 그래서 적절한 방어 코드를 넣어야 하는데, 원칙상으로는 페이지의 자바스크립트나 프로그래밍 언어에서 참조하는 외부 모든 인자에 대해서(HTTP 헤더의 모든 값 포함) 사용 전에 안전하게 필터링 해줘야 한다. 

 

  그런 부분을 충분히 설계하기 힘들다고 판단된다면, 아래와 같은 신뢰성 있는 기관에서 만든 필터를 이용하는 것이 현실적이다. 하지만 기본적인 XSS 에 대한 이해와, 자바스크립트의 이해, 방어의 한계 등을 잘 인지하고 있어야지 문제가 있을때 대처 할 수 있다는 것은 잊으면 안될것 같다(때로는 과하게 막아 프로그램과의 호환성을 위해서 해당 필터 라이브러리의 일부를 커스터마이즈 해야 될 수도 있을 듯 싶다). 또 위의 flask 예에도 보았듯이 괜찮은 웹 프레임워크나, 여러 자바스크립트 라이브러리를 쓰는 것도 해결책일 거 같다. 해당 라이브러리에 문제가 생긴다면 그쪽에서 빠르게 패치를 제공할 것이기 때문에 말이다(사실 보안은 어느정도 이렇게 신뢰성 있는 외부 프로젝트 들에 많이 의지하는 것 같다. 회사에서 구입해 사용하는 상용 백신 솔루션 같은 경우도 어찌보면 백신 회사의 지적인 프로젝트를 믿고 의지하는 것이라고 볼수 있다).

 

[Cross Site Scripting Prevention Cheat Sheet - OWASP]

https://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.md

 

[XSS 나무위키] - 4.1.1.2 에 네이버에서 제공되는 필터가 언급되어 있다.

https://namu.wiki/w/XSS

 

 

  reflected XSS 의 경우는 웹서버로 전달되는 모든 인자를 체크해야 하는 부분이지만, 사실 명확히 얘기하면, 프로그램이나 자바스크립트에서 실제 사용하는 인자가 실제 대상이다(뭐 미래에 사용 안하던 다른 인자가 사용될 수도 있지만 그때는 적절한 개발 단계의 보안 점검을 통해서 변화를 인지하고 팔로우업해야 될듯 하다).

 

  또 브라우저가 reflected XSS 를 인지할 수 있다는 것은 브라우저에 들어가는 입력과(여러 GET, POST 인자와 HTTP 헤더) 출력(서버로부터 전달되어 화면에 뿌려지는 HTTP 소스)을 알 수 있다면, reflected XSS 가 일어나는 후보 값들을 쉽게 발견할 수 있다는 것이다. 이 부분이 웹 취약점 스캐너가 XSS 를 찾는 원리라고 볼 수 있고, reflected XSS 또는 DOM based XSS 영역의 취약점을 점검하는데 많은 도움이 된다. 스캐너는사람이 하면 거의 불가능한 모든 인자를 리스트업 해서, 스크립트를 입력하고 결과 HTML 에서 해당 스크립트가 어떻게 되었는지 확인하여 판단을 해준다. 다만 이경우 앞에서 얘기한 실제 사용안하는 외부 값까지도 모두 검사하는 낭비 측면이 있지만 뭐 그건 자동화의 단점이자 장점인 듯 싶다.

 

  반대로 얘기하면 좀더 민감한 stored 같은 경우는 운이 좋지 않은 이상 스캐너로 발견하기 무척 힘들다. 해당 데이터에 우연히 접근되거나, 해당 데이터를 전송한 후에 바로 확인이 가능해야 한다. 전략적으로 스캐너를 이용해 stored XSS 테스트에 접근한다면, 기존 기능을 기반으로 입력 후 바로 디비에 저장된 결과를 보여주는 테스트 페이지를 만들어 스캐너를 돌린다든지 하는 작업이 필요할 듯 하다. 추가로 스캐너는 문제를 발견하는 것이지 문제가 없음을 100% 증명하는 툴은 아니라는 것을 인지하자. 스캐너가 사용한 수많은 XSS 패턴들이 모든 현존하는 XSS 패턴을 커버한다는 보장은 할수 없기 때문에, 스캐너 검사에 통과 되었더라도 여러 다양한 요소에 해당하는 취약점 패턴이 숨어있을 수도 있다.

 

 

 

 

7. 마무리 하면서

  추가로 위의 예에서는 유입 변수에 <script> 태그가 들어간 예제만 보았지만, 실제로는 해당 태그가 없어도 스크립트 동작이 가능한 경우가 많다. 적절한 코딩 방법은 아니지만 <script> 태그내의 스크립트 내용 중 일부를 외부 값을 사용하는 경우다(실제 이렇게 코딩하는 개발자들이 가끔 있다). 위의 플라스크 템플릿을 예를 들면 아래의 코드 형태일 것이다.

1
2
3
4
5
<script>
 
var a = {{ name|safe }};
....
</script>
cs

 

 

  name 변수에 "1;setInterval(function() {GetData();}, 3000);" 을 넣는다면 아래와 같은 반복 실행을 하는 문법이 똑같이 만들어질 것이다.  

1
2
3
4
5
<script>
 
var a = 1setInterval(function() {GetData();}, 3000);
....
</script>
cs

 

 

  뭐 여튼 결론적으로 스크립트 문제 및 인젝션 문제에 대해서는 요구사항이나 설계의 자유도가 높아질 수록 점점 방어하기는 복잡하게 되어 버린다. 모든 보안 분야가 마찬가지로 자유도를 높게 줄수록 방어하기는 더 어려워지며 포기해야할 트레이드 오프도 생길 수 있다. 결국 해당 부분의 영향을 최소화 하기 위해서는 대상이 되는 시스템과 요구사항, 기술, 프로세스, 관련된 사람들을 잘 이해하는 방법 밖에는 없을듯 싶다. 생각보다 실제에서는 보안은 명확하지 않은 애매한(놓여진 코드나, 요구사항, 여러 외부 환경에 따라 달라지는) 영역들이 상호 작용하는 분야이기도 한거 같다. 그나마 어플리케이션 보안 부분이 제일 명확한 면이 있어 보이는 듯 싶다. 점점 진행될 수록 앞의 다른 챕터의 얘기들이 조금씩 반복되곤 하는데 맨 처음 얘기한 듯이 보안의 원리가 결국 비슷비슷하기 때문이다. 어플리케이션 보안은 프로그램과 데이터 간의 술래잡기 같은 측면이 있다.

 

 

2019.3.3 by 자유로운설탕
cs

 

 

 

 

posted by 자유로운설탕
prev 1 next