[시큐어코딩 가이드] 2-1-4. 크로스사이트 스크립트(XSS)

크로스사이트 스크립트(XSS)

정의

  • 웹사이트에 악성 코드를 삽입하는 공격 방법
  • 일반적으로 애플리케이션 호스트 자체보다 사용자를 목표로 삼는다.
  • 공격자가 웹 응용프로그램을 속여 브라우저에서 실행될 수 있는 형식의 데이터(코드)를 다른 사용자에게 전달할 때 발생한다. 공격자가 임의로 구성한 기본 웹 코드 외에도 악성코드 다운로드, 플러그인 또는 미디어 콘텐츠를 이용할 수도 있다.

 

XSS 공격 유형

[Reflexctive XSS]

1. 공격자가 스크립트 주입이 가능한 취약점이 존재하는 웹사이트를 탐색

2. 방문자의 세션 쿠키를 탈취하는 악성 스크립트를 웹사이트에 주입

3. 사용자가 웹사이트 방문시 악성 스크립트가 실행됨

4. 웹사이트 방문객의 세션쿠기가 공격자에게 전달

ex) 공개 게시판, 피싱 이메일, 단축 URL, 실제와 유사한 URL 사용

 

[Persistent XSS]

1. 공격자가 악성코드를 포함하는 게시글 또는 댓글을 서버에 등록

2. 사용자가 웹서버에 접속한 후 악성 코드가 담긴 컨텐츠를 조회

3. 웹사이트는 사용자에게 악성코드를 포함하는 컨텐츠 전달

4. 사용자 브라우저는 컨텐츠 내부의 악성코드를 정상적인 응답으로 간주해 코드실행

5. 사용자의 민감한 정보가 공격자 서버로 전달

ex) 소셜 미디어 사이트 및 회원 그룹에서 공개적으로 표시되는 프로필 페이지

 

[DOM XSS]

1. 공격자가 악성코드를 포함하는 조작된 URL을 사용자에게 전달

2. 사용자가 링크를 클릭하면 웹서버에 악성 URL 요청

3. 요청받은 웹서버는 악성문자열을 포함하지 않는 응답을 사용자에게 전달

4. 사용자의 브라우저는 응답 데이터 내의 합법적인 스크립트를 실행하고, 악성스크립트를 페이지에 삽입

5. 사용자의 브라우저는 클라이언트 단에서 삽입된 페이지 내의 악성코드를 실행

6. 사용자의 민감한 정보가 공격자 서버로 전달

 

안전한 코딩기법

  • 문자열 치환 함수를 사용하여 &<>*‘/() 등을 &amp; &lt; &gt; &quot; &#x27; &#x2F; &#x28; &#x29; 로 치환하거나, html라이브러리의 escape()를 사용해 문자열을 변환해야 한다.
  • Django, Flask 프레임워크에서 각각 Django, Jinja2 템플릿을 사용할 시 XSS 공격에 악용될 수 있는 위험한 HTML 문자들을 HTML 특수문자 (HTML Entities)로 치환하는 기능을 제공하고 있어 프레임워크에서 제공하는 템플릿을 사용하는 경우 위협을 최 소화할 수 있다.

 

코드예제

Django의 “safestring(django.utils.safestring)”의 기능을 오용할 경우 Django의 XSS 공격에 대한 보호 정책이 무력화 될 수 있습니다. 다음은 안전하지 않은 코드 예제입니다.

#XSS(안전X)
def profile_link_bad(request):
    profile_url = request.GET.get('profile_url')
    profile_name = request.GET.get('profile_name')

    object_link = '<a href="{}">{}</a>'.format(profile_url, profile_name)

    object_link = mark_safe(object_link)
    return render(request, 'success.html', {'data': object_link})

 

profile_url=profile_url=javascript:alert('경고');&profile_name=tistory

 

템플릿 생성 시 HTML에서 위험한 것으로 간주되는 특수 문자를 모두 HTML 엔티티로 치환 하지만 mark_safe를 사용할 경우 이 정책을 따르지 않습니다. 따라서 mark_safe를 사용할 때는 각별한 주의가 필요합니다. 다음은 안전한 코드 예제입니다.

#XSS(안전O)
def profile_link_good(request):
    profile_url = request.GET.get('profile_url')
    profile_name = request.GET.get('profile_name')

    object_link = '<a href="{}">{}</a>'.format(profile_url, profile_name)
    
    return render(request, 'success.html', {'data': object_link})

 

profile_url=profile_url=javascript:alert('경고');&profile_name=tistory

 

 

 

입력데이터 검증 및 표현/크로스사이트 스크립트(XSS)
[참고문헌] Python 시큐어코딩 가이드(2022) / KISA(한국인터넷진흥원)