XSS(크로스 사이트 스크립팅) 공격과 방지 방법

최소희·2024년 5월 22일
0

프론트엔드 학습

목록 보기
15/23

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

XSS (Cross-Site Scripting)는 웹 보안 취약점 중 하나로, 공격자가 악의적인 스크립트를 다른 사용자의 브라우저에서 실행하도록 만드는 공격이다.
XSS 공격은 주로 웹 애플리케이션에서 사용자 입력을 제대로 검증하지 않거나 필터링하지 않는 경우 발생한다.

XSS의 유형

  1. 저장형 XSS (Stored XSS):
    • 공격자가 웹 애플리케이션의 데이터베이스에 악성 스크립트를 저장하는 형태이다. 이후 사용자가 해당 데이터를 요청할 때 스크립트가 실행된다.
    • 포럼 게시물, 댓글, 사용자 프로필 등에 악의적인 스크립트를 삽입하여 저장한다.
  2. 반사형 XSS (Reflected XSS):
    • 공격자가 악성 URL을 생성하여 피해자에게 전달하고, 피해자가 해당 URL을 클릭하면 스크립트가 실행된다. 이때 스크립트는 URL 매개변수 등을 통해 서버로 전송된 후 반사되어 실행된다.
    • 검색 결과 페이지, URL 매개변수를 포함한 사용자 입력 등이 반사형 XSS의 대표적인 사례이다.
  3. DOM 기반 XSS (DOM-based XSS):
    • DOM(Document Object Model)을 조작하여 발생하는 XSS로, 서버로의 요청이나 응답과는 관계없이 클라이언트 측에서 발생한다.
    • 클라이언트 측 스크립트에서 동적으로 생성된 HTML 요소를 조작하여 발생할 수 있다.

작동 원리

  1. 사용자 입력:
    • 공격자는 주로 웹 사이트의 폼, URL 매개변수 등의 사용자 입력을 이용한다.
  2. 악성 스크립트 삽입:
    • 공격자는 입력란에 악성 스크립트를 삽입하여 전달한다. 이 스크립트는 일반적으로 JavaScript 코드로 구성되며, 사용자의 브라우저에서 실행될 수 있도록 설계된다.
  3. 서버로 전달:
    • 사용자의 입력은 웹 애플리케이션 서버로 전달된다. 이때 악성 스크립트가 함께 전달되어 서버 측에서 처리된다.
  4. 렌더링:
    • 서버는 사용자의 입력을 받아들이고, 이를 웹 페이지에 반영하여 클라이언트에게 제공한다. 이때 입력된 악성 스크립트는 일반적으로 HTML 문서의 일부로 포함된다.
  5. 클라이언트 측 실행:
    • 클라이언트의 브라우저는 받은 HTML 문서를 해석하고 렌더링한다. 악성 스크립트는 사용자의 브라우저에서 실행되어 해당 사용자의 세션 쿠키, 브라우저 히스토리 등에 접근할 수 있게 된다.
  6. 공격 수행:
    • 실행된 악성 스크립트는 공격자가 의도한 작업을 수행한다. 이는 사용자의 정보를 탈취하거나 세션을 조작하는 등의 악의적인 행동으로 이어질 수 있다.

예시

  • 검색 결과 페이지
    • 사용자가 검색어를 입력하는 검색 입력란이 있다고 가정한다.
    • 사용자가 검색어로 <script>alert('XSS');</script>와 같은 악성 스크립트를 입력한다.
    • 이 입력이 서버로 전송되고, 서버는 검색어를 동적으로 처리하여 검색 결과 페이지를 생성한다.
    • 검색 결과 페이지에는 사용자의 입력이 반사되어 포함된다. 따라서 페이지를 렌더링하는 동안 악성 스크립트가 실행되어 사용자에게 'XSS'라는 경고창이 표시될 수 있다.

  • URL 매개변수를 포함한 사용자 입력
    • 웹 애플리케이션에서 URL에 매개변수를 사용하여 사용자에게 특정 페이지에 접근하도록 한다.
    • 예를 들어, http://example.com/page?search=<script>alert('XSS');</script>와 같은 URL을 사용자에게 제공한다.
    • 사용자가 해당 URL을 클릭하면, URL에 포함된 매개변수가 서버로 전달된다.
    • 서버는 이 매개변수를 동적으로 처리하여 해당 페이지를 생성한다. 이때 매개변수가 반사되어 페이지에 포함된다.
    • 따라서 페이지를 렌더링하는 동안 악성 스크립트가 실행되어 사용자에게 'XSS'라는 경고창이 표시될 수 있다.

  • 사용자 입력
    • 사용자의 입력이 JavaScript로 동적으로 생성된 HTML 요소에 반영될 때, 공격자가 입력란에 <img src="doesnotexist">와 같은 스크립트를 삽입하여, 해당 요소가 렌더링될 때 'XSS'라는 경고창이 표시될 수 있다.
      <!-- 사용자 입력을 동적으로 생성된 HTML 요소에 반영하는 코드 -->
      <div id="dynamicContent"></div>
      
      <script>
      // 사용자 입력을 동적으로 생성된 HTML 요소에 반영하는 스크립트
      const userInput = "<img src='doesnotexist' onerror='alert(\"XSS\")'>";
      document.getElementById('dynamicContent').innerHTML = userInput;
      </script>
      

XSS 방지법

1. 입력 데이터의 이스케이프 (Escape Input Data)

  • HTML 이스케이프 (HTML escaping): 사용자 입력을 HTML 요소에 반영하기 전에 HTML 특수 문자를 이스케이핑하여 스크립트 실행을 방지한다.
    이를 통해 입력된 HTML 태그나 스크립트가 그대로 렌더링되는 것을 방지할 수 있다.
    // XSS에 취약함
    element.innerHTML = userInput;
    
    // XSS에 안전함
    element.textContent = userInput;
  • textContentinnerText, innerHTML 의 차이점 정리

    ▶️ textContentinnerText의 차이점

    • 모든 요소의 콘텐츠 vs 사람이 읽을 수 있는 요소
      • textContent<script><style> 요소를 포함한 모든 요소의 콘텐츠를 가져온다.
      • 반면, innerText는 사람이 읽을 수 있는 요소만 처리한다.

    • 노드 반환 방식과 리플로우
      리플로우(Reflow):  HTML Element의 길이, 위치에 대한 픽셀 값을 연산해서 Layout을 생성하는 과정
      • textContent는 노드의 모든 텍스트를 반환한다.
      • innerText는 스타일링을 고려하며, 숨겨진 요소의 텍스트는 반환하지 않는다.
      • innerText를 읽을 때 최신 계산값을 반영하기 위해 리플로우가 발생할 수 있다. 리플로우는 성능에 영향을 미칠 수 있으므로 가능하면 피하는 것이 좋다.

    ▶️ textContentinnerHTML의 차이점

    • 텍스트 vs HTML

      • innerHTML은 HTML을 반환한다. 반면 textContent는 텍스트를 반환하며, HTML을 분석할 필요가 없으므로 성능이 더 좋다.
    • XSS 공격의 위험성
      - textContent는 XSS 공격의 위험이 없다. 따라서 보안 측면에서도 더 안전하다.

      예제

      HTML 구조:

      <div id="divA">This is <span>some</span> text!</div>

      textContent로 요소의 텍스트 콘텐츠를 가져오기:

      const text = document.getElementById("divA").textContent;
      // text 변수는 이제 'This is some text!'를 가리킨다.

      textContent로 요소의 텍스트 내용을 설정하기:

      document.getElementById("divA").textContent = "This text is different!";
      // divA의 HTML은 이제 <div id="divA">This text is different!</div>가된다.
      

      innerText와 textContent 렌더링 차이점 보기 (MDN 문서 예제 코드)

2. 출력 데이터의 적절한 이스케이프 (Escape Output Data):

  • HTML 이스케이프 (HTML escaping): 서버에서 클라이언트로 전달되는 데이터를 출력할 때도 HTML 특수 문자를 이스케이핑하여 XSS 공격을 방지한다. 대부분의 웹 프레임워크에서는 이스케이핑을 자동으로 수행한다.
    // Bad practice (XSS vulnerable)
    response.send(`<p>${userInput}</p>`);
    
    // Good practice (XSS safe)
    response.send(`<p>${escapeHtml(userInput)}</p>`);
    
    function escapeHtml(unsafe) {
        return unsafe.replace(/[&<"']/g, function(match) {
            return {
                '&': '&amp;',
                '<': '&lt;',
                '>': '&gt;',
                '"': '&quot;',
                "'": '&#39;'
            }[match];
        });
    }
    • React에서의 HTML 이스케이프 React는 JSX를 사용하여 UI를 생성하는 경우에도 HTML 이스케이핑을 자동으로 수행한다.
      JSX는 JavaScript의 확장 문법으로, HTML과 유사한 구조를 사용하여 UI를 정의할 수 있다.
      이 때문에 React에서는 사용자가 제공한 데이터를 UI에 렌더링할 때 HTML 이스케이핑을 수행하여 XSS 공격을 방지한다. 예를 들어, React에서 다음과 같이 JSX를 사용하여 변수를 UI에 렌더링하는 경우:
      const userInput = '<script>alert("XSS attack!")</script>';
      const element = <div>{userInput}</div>;
      React는 자동으로 userInput의 값에 포함된 HTML 특수 문자를 이스케이핑하여 다음과 같이 렌더링한다.
      <div>&lt;script&gt;alert("XSS attack!")&lt;/script&gt;</div>
      따라서 React를 사용하여 UI를 개발할 때에도 HTML 이스케이핑은 자동으로 수행되므로 XSS 공격에 대한 보호가 강화된다.
      하지만, React를 사용할 때에도 사용자 입력을 신뢰하지 않고 항상 안전한 방법으로 처리하는 것이 중요하다.

3. 콘텐츠 보안 정책 (Content Security Policy, CSP):

  • CSP 설정: CSP를 설정하여 허용되지 않은 스크립트의 실행을 방지하고, 안전한 리소스의 로드를 보장한다. CSP를 사용하면 스크립트 실행 출처를 명시하고, 허용되지 않은 스크립트 실행을 차단하여 XSS 공격을 방지할 수 있다.
    Content-Security-Policy: default-src 'self';

4. 입력 데이터의 검증 (Input Data Validation):

  • Whitelist Validation: 입력 데이터를 특정한 패턴이나 형식에 맞게 검증하여 허용된 입력만을 허용한다. 특히 URL 파라미터를 사용할 때, 허용된 문자만을 허용하도록 검증한다.
    // Bad practice (XSS vulnerable)
    const searchTerm = req.query.searchTerm;
    
    // Good practice (XSS safe)
    const searchTerm = validateSearchTerm(req.query.searchTerm);
    
    function validateSearchTerm(searchTerm) {
        // Whitelist validation: Allow only alphanumeric characters
        return searchTerm.replace(/[^a-zA-Z0-9]/g, '');
    }

5. 세션 쿠키의 보호:

  • HttpOnly Cookie: 세션 쿠키를 HttpOnly 속성으로 설정하여 JavaScript에서 접근할 수 없도록 한다. 이를 통해 XSS 공격으로부터 세션 쿠키를 탈취하는 것을 방지한다.
    Set-Cookie: session=sessionId; HttpOnly;

참고 자료
React 공식 문서
MDN 공식 문서
OWASP/CheetSheetSeries

profile
프론트엔드 개발자 👩🏻‍💻

0개의 댓글