고도연
Study

정리하기 파일

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

14.1 리액트에서 발생하는 크로스 사이트 스크립팅(XSS)

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

dangerouslySetInerHTML prop

dangerouslySetInerHTML은 특정 브라우저 DOM의 innerHTML을 특정한 내용으로 교체할 수 있는 방법이다.

useRef를 활용한 직접 삽입

useRef를 활용하면 직접 DOM에 접근할 수 있으므로 이 DOM에 앞서와 비슷한 방식으로 innerHTML에 보안취약점이 있는 스크립트를 삽입하면 동일한 문제가 발생한다.

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

제 3자가 삽입할 수 있는 HTML을 안전한 HTML코드로 한 번 치환하는 것이다. 이러한 과정을 새니타이즈(sanitize) 또는 이스케이프(escape)라고 한다.

이와 관련된 유명한 라이브러리로는 다음과 같은 것이 있다.

  • DOMpurity
  • sanitize-html
  • js-xss

sanitize-html은 허용할 태그와 목록을 일일히 나열하는 허용 목록(allow list) 방식을 채택하기 때문에 안전하다.

단순히 보여줄 때뿐만 아니라 사용자가 콘텐츠를 저장할 때도 한번 이스케이프 과정을 거치는 것이 더 효율적이고 안전하다.

서버는 ‘클라이언트에서 사용자가 입력한 데이터는 일단 의심한다’라는 자세로 클라이언트의 POST 요청에 있는 HTML을 이스케이프하는 것이 제일 안전하다.

14.2 getServerSideProps와 서버 컴포넌트를 주의하자

  • 서버에는 일반 사용자에게 노출되면 안 되는 정보들이 담겨 있기 때문에 클라이언트에 정보를 내려줄 때는 조심해야 한다.
  • getServerSideProps가 반환하는 값 또는 서버 컴포넌트가 클라이언트 컴포넌트에 반환하는 props는 반드시 필요한 값으로만 철저하게 제한되어야 한다. 보안 측면의 이점뿐만 아니라 성능 측면에서도 이점을 가져다 준다.

14.3 <a>태그의 값에 적절한 제한을 둬야 한다

웹 개발 시에 <a>태그의 href에 javascript:로 시작하는 자바스크립트 코드를 넣으면 <a>태그의 기본 기능 href로 선언된 URL로 페이지를 이동하는 것을 막고 onClick 이벤트와 같이 별도 이벤트 핸들러만 작동시키기 위한 용도로 주로 사용된다.

페이지 이동 없이 어떠한 핸들러만 작동시키고 싶다면 <a>보다는 button을 사용하는 것이 좋다.

14.4 HTTP 보안 헤더 설정하기

HTTP 보안 헤더란 브라우저가 렌더링하는 내용과 관련된 보안 취약점을 미연에 방지하기 위해 브라우저와 함께 작동하는 헤더를 의미한다.

14.4.1 Strict-Transport-Security

HTTP의 Strinct-Transport-Security 응답 헤더는 모든 사이트가 HTTPS를 통해 접근해야 하며, 만약 HTTP로 접근하는 경우 이러한 모든 시도는 HTTPS로 변경되게 한다.

Strict-Transport-Security: max-age=<expire-time>; includeSubDomains

<expire-time>은 이 설정을 브라우저가 기억해야 하는 시간을 의미한다.

14.4.2 X-XSS-Protection

X-XSS-Protection은 비표준 기술로, 현재 사파리와 구형 브라우저에서만 제공되는 기능이다.

이 헤더는 페이지에서 XSS 취약점이 발견되면 페이지 로딩을 중단하는 헤더다.

14.4.3 X-Frame-Options

X-Frame-Options는 페이지를 frame, iframe, embed, object 내부에서 렌더링을 허용할지를 나타낼 수 있다.

네이버에 X-Frame-Options: deny 옵션이 있기 때문에 제 3의 페이지에서 <iframe>으로 삽입되는 것을 막는 역할을 한다.

  • DENY: 만약 위와 같은 프레임 관련 코드가 있다면 무조건 막는다.
  • SAMEORIGIN: 같은 origin에 대해서만 프레임을 허용한다.

14.4.4 Permissions-Policy

Permissions-Policy는 웹사이트에서 사용할 수 있는 기능과 사용할 수 없는 기능을 명시적으로 선언하는 헤더다.

여기서 말하는 기능이란 카메라나 GPS와 같이 브라우저가 제공하는 기능을 말한다.

# 모든 geolocation 사용을 막는다.
Permissions-Policy: geolocation=()
 
# geolocation을 페이지 자신과 몇 가지 페이지에 대해서만 허용한다.
Permissions-Policy: geolocation=(self "https://a.yceffort.kr" "https://b.yceffort.kr")
 
# 카메라는 모든 곳에서 허용한다.
Permissionss-Policy: camera=*;

14.4.5 X-Content-Type-Options

X-Content-Type-Options란 Content-type 헤더에서 제공하는 MIME 유형이 브라우저에 의해 임의로 변경되지 않게 하는 헤더다. 즉, 웹서버가 브라우저에 강제로 이 파일을 읽는 방식을 지정하는 것이 이 헤더다.

  • MIME란 Multipurpose Internet Mail Extensions의 약자로, Content-type의 값으로 사용된다.

다음과 같은 헤더를 설정해 두면 파일의 타입이 CSS나 MIME이 text/css가 아닌 경우, 혹은 파일 내용이 script나 MIME 타입이 자바스크립트 타입이 아니면 차단하게 된다.

X-Content-Type-Options: nosniff

14.4.6 Referrer-Policy

HTTP 요청에는 Referer라는 헤더가 존재하는데, 이 헤더에는 현재 요청을 보낸 페이지의 주소가 나타난다. Referrer-Policy 헤더는 이 Referer 헤더에서 사용할 수 있는 데이터를 나타낸다.

(Referer가 아닌 Referrer인 이유는 오타임을 뒤늦게 발견했기 때문)

https://yceffort.kr (opens in a new tab) 라는 주소의 출처는 다음과 같이 구성돼 있다.

  • scheme: HTTPS 프로토콜을 의미한다.
  • hostname: yceffort.kr (opens in a new tab)이라는 호스트명을 의미한다.
  • port: 443 포트를 의미한다.

구글에서는 이용자의 개인정보 보호를 위해 strict-origin-when-cross-origin 혹은 그 이상을 명시적으로 선언해 둘 것을 권고한다.

14.4.7 Content-Security-Policy

콘텐츠 보안 정책(Content-Security-Policy)은 XSS 공격이나 데이터 삽입 공격과 같은 다양한 보안 위협을 막기 위해 설계됐다. 사용 가능한 모든 지시문은 웹 표준을 정의한 W3에서 확인할 수 있다.

14.4.8 보안 헤더 설정하기

  • Next.js

    HTTP 경로별로 보안 헤더를 적용할 수 있다. 이 설정은 next.config.js에서 추가할 수 있다.

  • NGINX

    정적인 파일을 제공하는 NGINX의 경우 경로별로 add_header 지시자를 사용해 원하는 응답 헤더를 추가할 수 있다.

14.4.9 보안 헤더 확인하기

보안 헤더의 현황을 알려주는 https://securityheaders.com/ (opens in a new tab) 에서 확인할 수 있다.

14.5 취약점이 있는 패키지의 사용을 피하자

개발자가 프로젝트의 package.json에 어떤 패키지가 있는지 정도는 파악 가능하지만 package-lock.json의 모든 의존성을 파악하는 것은 사실상 불가능에 가깝다. 따라서 깃허브의 Dependabot이 발견한 취약점은 필요하다면 빠르게 업데이트해 조치해야 한다.

14.6 OWASP Top 10

OWASP은 Open Worldwide Application Security Project라는 오픈소스 웹 애플리케이션 보안 프로젝트를 의미한다. 주로 웹에서 발생할 수 있는 정보 노출, 악성 스크립트, 보안 취약점 등을 연구하며, 주기적으로 10대 웹 애플리케이션 취약점을 공개하는데 이를 OWASP Top 10이라고 한다.



15장. 마치며

15.1 리액트 프로젝트를 시작할 때 고려해야 할 사항

유지보수 중인 서비스라면 리액트 버전을 최소 16.8.6에서 최대 17.0.2로 올려두자

리액트 16.8에서는 훅이 소개됐고, 훅 덕분에 함수형 컴포넌트의 개념이 정립됐다.

17 버전은 리액트 팀에서 공언한 대로 새로운 기능 출시 및 호환성이 깨지는 변경 사항을 최소한으로 맞춘 업데이트다.

인터넷 익스플로러 11 지원을 목표한다면 각별히 더 주의를 기한다

  • 리액트 : 리액트는 18 버전부터 인터넷 익스플로러 11을 지원하지 않기로 했다.
  • Next.js : Next.js는 13 버전부터 인터넷 익스플로러 11을 지원하지 않기로 했다.
  • query-string : 주소의 쿼리 문자열을 다루는 대표적인 라이브러리인 query-string도 6.x 버전부터 인터넷 익스플로러 11을 지원하지 않는다.

서버 사이드 렌더링 애플리케이션을 우선적으로 고려한다

싱글 페이지 애플리케이션은 대부분의 경우 라이트하우스나 WebPageTest, 구글 개발자 도구에서 좋은 결과를 얻기 어렵다.

관리자와 같이 제한적인 사용자에게 빠르게 웹페이지를 제공해야 하는 경우, 소규모 웹페이지를 만드는 경우, 서버를 준비해야 할 상황이 여의치 않은 경우에만 싱글 페이지 애플리케이션을 만들 것을 추천한다.

많은 사용자를 감당해야 하고, 서버를 준비할 수 있는 충분한 여유가 된다면 시작부터 서버 사이트 렌더링을 고려하는 것이 좋다.

상태 관리 라이브러리는 꼭 필요할 때만 사용한다

관리해야 할 상태가 많은 애플리케이션이라면 상태 관리를 사용하는 것이 좋다. 그렇지 않고, 비교적 정적인 애플리케이션이라면 상태 관리 라이브러리가 굳이 필요하지 않을 수도 있다.

리액트 의존성 라이브러리 설치를 조심한다

어떠한 기능 구현을 위해 라이브러리를 설치할 때 , 리액트에 의존적인 라이브러리를 설치하려고 하는 경우가 있을 것이다. 이때 반드시 peerDependencies가 설치하고자 하는 프로젝트의 리액트 버전과 맞는지 확인해야 한다.

훅을 사용한다면 반드시 16.8 버전 이상인지 확인하고, 리액트 18을 사용하고 싶다면 사용할 라이브러리 또한 18을 지원하는지 반드시 확인하자.