Stored XSS 실습 및 대응 방안

실습 환경


claude code를 이용해 생성한 XSS 취약점 테스트용 Flask 서버를 구성한다.


실습


댓글 작성 기능을 가진 웹 애플리케이션이다.


[작성자] 혹은 [댓글 내용] 부분에 실행하고 싶은 스크립트를 삽입한다. 실습 과정에서는 경고 창을 띄우는 “alert(1)”을 사용할 것이다.


댓글을 등록하면, 악성 스크립트가 포함된 게시글이 서버 저장소에 저장되어, 해당 댓글을 요청하는 모든 사용자는 악성 스크립트를 실행하게 된다. 실습 과정에서는 “alert(1)” 스크립트를 서버 저장소에 저장 했으므로, “1”을 출력하는 알림 창을 띄우게 된다.


서버 저장소를 확인해보면 악성 스크립트가 검증 없이 그대로 저장된 것을 확인할 수 있다. 그리고 출력 과정에서도 검증을 하지 않고 출력되어, 악성 스크립트가 실행될 수 있다.


취약점이 발생한 이유


  1. 공격자는 악성 스크립트가 포함된 댓글 등록을 웹 애플리케이션에 요청한다.
  2. 악성 스크립트가 담긴 댓글을 검증하지 않고 데이터베이스에 저장한다.
  3. 피해자는 댓글이 포함된 웹 페이지를 요청한다.
  4. 악성 스크립트가 담긴 댓글을 데이터베이스에 요청한다.
  5. 데이터베이스는 악성 스크립트가 담긴 댓글을 응답한다.
  6. 악성 스크립트가 담긴 댓글을 검증하지 않고 동적 페이지를 구성 후, 피해자에게 응답한다.

위 과정에서 붉은색으로 강조 표시한 텍스트를 보면, 사용자 입력 값을 데이터베이스에 저장할 때와 동적 페이지를 구성할 때, 이 두 가지로 나눌 수 있다. 이것에 대한 대응 방식으로 전위 처리 방식, 후위 처리 방식이 있는데, 이 두 방식에 검증 로직이 존재하지 않기 때문에 취약점이 발생하는 것이다.


대응 방안


후위 처리 방식으로 대응할 경우, 모든 출력 구간에 대해서 로직을 작성해야 한다. 전위 처리 방식은 데이터베이스에 저장하는 애플리케이션에 대해서 시큐어 코딩을 적용하면 되므로, 이번 실습에서는 전위 처리 방식에 대해서 시큐어 코딩을 진행한다.

정규 표현식과 알림 창을 사용할 것이므로 패키지를 추가한다.

from flask import flash
import re
import secrets


입력 값이 없거나, 입력 값 [< > ” ‘ & / \]을 검증할 함수를 추가한다.


댓글을 데이터베이스에 저장하는 기능에 검증 로직을 수행하도록 코드를 추가한다.


검증을 통해 잘못된 입력 값을 탐지하는 경우, 웹 페이지 상단에 에러 메시지를 flash를 통해 출력하려면 stored.html도 수정해야 한다.


시큐어 코딩 후 실습


시큐어 코딩 전 취약한 상태에서는 실습한 내용과 같이, 자바스크립트가 실행되어 “1”을 출력하는 알림 창이 출력됐다.

시큐어 코딩을 적용하면, 실습에 사용한 페이로드를 그대로 적용하니, 댓글이 저장되지 않고 에러 메시지가 출력되는 것을 확인할 수 있다.

Published by