웹사이트 보안을 위한 리액트와 웹페이지 보안 이슈

shooting star·2024년 7월 12일
post-thumbnail

웹사이트 보안을 위한 리액트와 웹페이지 보안 이슈

웹사이트의 성능만큼 중요한 것이 바로 웹사이트의 보안입니다. 최근 각종 IT 보안 이슈로 인해 개인정보 유출, 시스템 마비 등의 문제가 발생하면서 일반 소비자나 웹사이트 방문자들도 웹사이트의 안전성과 보안에 대한 의구심이 커지고 있습니다. 코드의 규모가 증가함에 따라 보안 취약점에 노출될 확률도 증가합니다. 이번 글에서는 리액트를 기반으로 한 웹 애플리케이션을 만드는 프런트엔드 개발자가 신경 써야 할 다양한 보안 이슈를 살펴보겠습니다.

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

크로스 사이트 스크립팅(Cross-Site Scripting, XSS)은 웹 애플리케이션에서 가장 많이 보이는 취약점 중 하나로, 웹사이트 개발자가 아닌 제 3자가 웹사이트에 악성 스크립트를 삽입해 실행할 수 있는 취약점을 의미합니다. 게시판과 같이 사용자가 입력할 수 있고, 이 입력을 다른 사용자에게 보여줄 수 있는 경우에 발생합니다. 예를 들어 어떤 게시판에 사용자가 다음과 같은 글을 올린다고 가정해보겠습니다:

<script>alert('XSS 공격!');</script>

이 스크립트는 다른 사용자가 해당 게시판 페이지를 방문할 때 실행되어, 쿠키를 획득해 사용자의 로그인 세션을 탈취하거나 사용자의 데이터를 변경하는 등 각종 위험성을 초래할 수 있습니다.

리액트에서 XSS 문제

리액트에서는 주로 dangerouslySetInnerHTML prop을 사용하거나 useRef를 통해 직접 DOM에 접근할 때 XSS 이슈가 발생할 수 있습니다.

dangerouslySetInnerHTML

function MyComponent() {
  const rawHTML = "<div><script>alert('XSS 공격!');</script></div>";
  return <div dangerouslySetInnerHTML={{ __html: rawHTML }} />;
}

dangerouslySetInnerHTML prop은 HTML 문자열을 그대로 DOM에 삽입하기 때문에, 악성 스크립트가 포함된 경우 XSS 공격에 노출될 수 있습니다.

useRef를 활용한 직접 삽입

import React, { useRef, useEffect } from 'react';

function MyComponent() {
  const ref = useRef();

  useEffect(() => {
    ref.current.innerHTML = "<div><script>alert('XSS 공격!');</script></div>";
  }, []);

  return <div ref={ref}></div>;
}

useRef를 사용해 DOM에 직접 접근하여 HTML을 삽입할 때도 동일한 문제가 발생할 수 있습니다.

리액트에서 XSS 문제를 피하는 방법

가장 확실한 방법은 제 3자가 삽입할 수 있는 HTML을 안전한 HTML 코드로 변환하는 것입니다. 이러한 과정을 새니타이즈(Sanitize) 또는 이스케이프(Escape)라고 합니다. 가장 많이 사용되는 라이브러리로는 DOMPurify, sanitize-html, js-xss 등이 있습니다.

DOMPurify를 사용한 예제

import DOMPurify from 'dompurify';

function MyComponent() {
  const rawHTML = "<div><script>alert('XSS 공격!');</script></div>";
  const sanitizedHTML = DOMPurify.sanitize(rawHTML);

  return <div dangerouslySetInnerHTML={{ __html: sanitizedHTML }} />;
}

DOMPurify는 HTML 문자열을 안전하게 변환하여 XSS 공격을 방지합니다.

서버 사이드 렌더링과 보안

서버 사이드 렌더링(SSR)과 서버 컴포넌트는 성능 이점을 제공하지만, 동시에 보안 취약점에 노출될 수 있습니다. 예를 들어, getServerSideProps를 통해 서버에서 데이터를 가져오는 경우 반환되는 props 값은 모두 사용자의 HTML에 기록되며, 전역 변수로 등록되어 스크립트로 접근할 수 있는 보안 위협에 노출됩니다.

export async function getServerSideProps(context) {
  const cookie = context.req.headers.cookie || '';
  return {
    props: {
      token: cookie.split('token=')[1] || null,
    },
  };
}

getServerSideProps가 반환하는 값은 반드시 필요한 값으로 제한해야 하며, 민감한 정보는 포함하지 않도록 주의해야 합니다.

HTTP 보안 헤더 설정하기

HTTP 보안 헤더는 브라우저가 렌더링하는 내용과 관련된 보안 취약점을 방지하기 위해 브라우저와 함께 작동하는 헤더입니다. 이는 웹사이트 보안의 기초적인 부분으로, HTTP 보안 헤더만 효율적으로 사용해도 많은 보안 취약점을 방지할 수 있습니다. 대표적인 HTTP 보안 헤더는 다음과 같습니다:

X-Frame-Options

X-Frame-Options 헤더는 페이지가 frame, iframe, embed, object 요소 내에서 렌더링되는 것을 방지합니다. 이는 클릭재킹(Clickjacking) 공격을 방지하는 데 유용합니다. 클릭재킹이란 사용자가 자신도 모르게 클릭하게 유도하여 의도치 않은 동작을 수행하게 하는 공격입니다.

X-Frame-Options 헤더의 옵션:

  • DENY: 페이지가 어떤 경우에도 프레임에 삽입되지 않도록 합니다.
  • SAMEORIGIN: 동일한 도메인에서만 프레임에 삽입될 수 있도록 합니다.

예제:

const securityHeaders = [
  {
    key: 'X-Frame-Options',
    value: 'DENY',
  },
];

Content Security Policy (CSP)

콘텐츠 보안 정책(CSP)은 XSS 공격이나 데이터 삽입 공격과 같은 다양한 보안 위협을 막기 위해 설계되었습니다.

const securityHeaders = [
  {
    key: 'Content-Security-Policy',
    value: "default-src 'self'; script-src 'self' 'unsafe-inline'; object-src 'none'",
  },
];

기타 보안 헤더

  • X-Content-Type-Options: 브라우저가 선언된 Content-Type에 따라 리소스를 처리하도록 강제합니다.
  • Referrer-Policy: HTTP 요청 시 Referer 헤더에 포함될 정보를 제어합니다.
  • Permissions-Policy: 웹사이트에서 사용할 수 있는 기능과 사용할 수 없는 기능을 명시적으로 선언합니다.

이러한 보안 헤더를 Next.js 프로젝트의 next.config.js 파일에 설정하여 적용할 수 있습니다.

module.exports = {
  async headers() {
    return [
      {
        source: '/(.*)',
        headers: [
          {
            key: 'Content-Security-Policy',
            value: "default-src 'self'; script-src 'self' 'unsafe-inline'; object-src 'none'",
          },
          {
            key: 'X-Frame-Options',
            value: 'DENY',
          },
          {
            key: 'X-Content-Type-Options',
            value: 'nosniff',
          },
          {
            key: 'Referrer-Policy',
            value: 'strict-origin-when-cross-origin',
          },
          {
            key: 'Permissions-Policy',
            value: 'geolocation=(self), microphone=()',
          },
        ],
      },
    ];
  },
};

결론

웹사이트의 보안은 성능만큼이나 중요합니다. 리액트 애플리케이션을 개발할 때 XSS와 같은 보안 이슈를 주의 깊게 다루어야 하며, dangerouslySetInnerHTMLuseRef 사용 시 특별히 신경 써야 합니다. 또한, 서버 사이드 렌더링과 서버 컴포넌트를 사용할 때 민감한 정보를 노출하지 않도록 주의해야 합니다. 마지막으로, HTTP 보안 헤더를 설정하여 웹사이트의 보안을 강화할 수 있습니다. 이러한 방법들을 통해 안전한 웹사이트를 구축하고 사용자 정보를 보호할 수 있습니다.

0개의 댓글