All Articles

프론트엔드 개발자를 위한 10가지 보안 관련 팁


이 글은 원문에서 참고했고 약간의 내 생각을 의역으로 덧붙인 글입니다.
This artice is come from this Site


웹 보안은 Front-end 개발자들이 종종 간과하거나 잘 생각하지 못하는 듯하다. 보통 웹사이트 퀄리티를 체크할 때 많이 보는 것은 성능, 검색엔진 최적화(SEO), 시맨틱 태그 같은 접근성등 측정 가능한 수치들을 기반으로 본다. 하지만 어떤 사용자가 악의적으로 공격을 할 때 이것을 잘 방어할 수 있는지에 대한 것은 사실 잘 측정하기도 어렵고 보이지도 않는다.

보통 개인정보 같은 민감한 데이터는 서버측에 저장되어 있을테고, 물론 백엔드 개발자가 이를 잘 보호하기 위해 노력해야 한다. 하지만 결국 Front-end 개발자와 Back-end 개발자가 둘 다 보호할 책임이 있다. 집 안 금고(Back-end)에 개인정보를 잘 저장해놓을 수는 있지만, Front-end가 현관문 열쇠를 가지고 있고, 훔치기도 쉽다.


사용자 데이터를 안전하게 보호해야할 책임은
Front-end와 Back-end 둘 다 가지고 있다.

악의적인 사용자가 Front-end 어플리케이션을 망가뜨리기 위해 할 수 있는 공격 루트는 여러가지가 있다. 하지만 response 헤더 몇 개를 사용하고 우수한 개발 방법을 잘 따르기만 한다면 이런 공격들을 대부분 막아낼 수 있다. 이 포스팅에서는 웹 애플리케이션 보호를 위해 할 수 있는 10가지 쉬운 작업들을 다룬다.

결과 측정

웹사이트 보안을 개선하기 위해서는 변경사항에 대해 피드백을 받는 것이 중요하다. “좋은 개발 관행”을 갖는 것은 좀 어렵다. 경험이 쌓여야 하니까. 하지만 보안 헤더는 쉽고 정확하게 측정 할 수 있다.
구글 Chrome에서 lighthouse를 사용해서 성능, SEO, 접근성 점수를 측정할 수 있는 것처럼 비슷하게 보안 기능을 측정 할 수 있는 사이트가 있다.

SecurityHeaders은 response header를 기반으로 보안 점수를 제공해 준다.

블로그 안전성 테스트
A+도 있고 F도 있는데, 내 블로그의 경우 netlify를 통해 자동으로 호스팅 해준다. 내 블로그는 B를 맞았는데, 안타깝게도 대부분의 사이트는 D인 것 같다. 네이버도 , 다음도 그런데 구글도 D다.

response 헤더에 대해 알아둘 사항

response 헤더를 다루는 것은 Backend의 작업이었다. 오늘날에는 웹 애플리케이션을 Zeit 또는 Netlify와 같은 “서버리스”클라우드 플랫폼에 배포하는 경우도 많아지고 있다.(Firebase도 마찬가지)
이에 대해 적절한 response 헤더를 반환하도록 구성하는 것이 front-end 책임이 된다.
내가 만드는 웹어플리케이션의 클라우드 호스팅 제공 업체가 어떤 response 헤더를 사용하는지 이에 대한 작동하는 방식을 배우고 적절하게 구성하면 된다.

보안 대책



1. 강력한 컨텐츠 보안 정책(CSP) 사용


컨텐츠 보안 정책 (CSP)은 front-end 애플리케이션의 안전을 위해 시작할 수 있는 첫 단계라고 할 수 있다. CSP는 Mozila 재단에서 만든 표준인데, XSS (Cross-Site Scripting) 및 클릭 재킹 (clickjacking)을 포함하여 특정 유형의 코드 삽입 공격을 탐지하고 막아준다.

강력한 CSP는 잠재적으로 유해한 인라인 코드 실행을 비활성화해주고 외부 리소스가 로드되는 도메인을 제한하는 것이 가능하다. Content-Security-Policy 헤더를 세미콜론으로 구분지어서 사용할 수 있다. 아래는 웹 사이트가 외부 리소스에 액세스할 필요가 없는 경우 헤더설정이다.

Content-Security-Policy: default-src 'none'; script-src 'self';
img-src 'self'; style-src 'self'; connect-src 'self';

여기서는 script-src, img-src, style-src 및 connect-src 지시어를 self로 설정했다.
그러니까 이렇게 하게 되면 document를 로드 할때 css, script, image같은 리소스들은 HTML 문서가 제공되는 곳과 불러오는 곳이 동일해야 한다는 것이다. Defualt CSP 지침은 default-src로 설정하면 된다. 기본 동작이 URL에 대한 연결을 제한해야 하기 때문에 none으로 설정했다.

그러니까 보통 콘텐츠 보안정책은 동일출처 원칙이 기본이긴 하다. 예를 들어 https://yohanpro.comhttps://yohanpro.com의 데이터에만 액세스 할 수 있는 권한이 있다. https://example.com에는 액세스 권한이 없다.

그런데 웹어플리케이션을 만들다보면 대부분 다 알겠지만, 요즘 어플리케이션을 만들때에는 다른 곳에서 자원을 가져다 쓰기 마련이다. Google developer 콘솔에서 API를 가져오든, AWS S3에서 가져오든 말이다. 하지만 나중에 이런 곳의 도메인을 허용하도록 설정하더라도 일단 가장 엄격하게 설정해 놓는 것이 필요하다.

CSP 지침의 전체 목록은 MDN 웹사이트에서 찾을 수 있다.


2. XSS 보호 모드 사용


사용자 입력에서 악성 코드가 입력되는 것을 감지한다면 "X-XSS-Protection": "1; mode = block"헤더를 사용하면 된다. 이는 브라우저가 response를 차단하도록 만들 수 있다.

물론 요즘 최신 브라우저들은 XSS 보호모드가 기본으로 되어있긴 하다. 하지만 CSP 헤더를 지원하지 않는 브라우저들(IE라던지 IE라던지…)을 위해서 X-XSS-Protection 헤더를 포함해주는 것이 좋다.


3. 클릭재킹 공격을 방지하기 위해 iframe Embed 막기


클릭재킹 공격을 막기 위해서 iframe 임베딩을 막는 것이 필요하다. 클릭재킹 공격은 요즘 뉴스에도 나와서 핫한 공격이다. 쉽게 말하면 다른 사이트인척 속이는 것이다. 국민은행 피싱사이트가 예이다.

피싱 사이트 예시

이것 역시 헤더에 X-Frame-option을 줌으로써 해결할 수 있다.

"X-Frame-Options": "DENY"

또한 frame-ancestors를 사용할 수도 있는데, 이건 현재 페이지를 삽입할 수 있는 소스를 지정할 수 있다.<frame>, <iframe>, <embed>, <applet> 태그에 적용된다.

4. 브라우저 기능 및 API에 대한 액세스 제한하기


우리가 만든 웹사이트에서 필요하지 않은 것들에 대한 접근을 제한하는 것은 좋은 보안 습관이다. 이미 CSP를 사용하여 웹사이트가 접속할 수 있는 도메인 수를 제한하기 위해 이 원칙을 적용했지만, 브라우저 기능에도 이걸 넣을 수 있다.
브라우저가 Feature-Policy 헤더를 사용하여 애플리케이션이 필요로 하지 않는 특기능과 API에 대한 액세스를 거부하도록 만들 수 있다.

똑같이 세미콜론 구분자를 사용해서 문자열로 생성하면 된다.

"Feature-Policy": "accelerometer 'none'; ambient-light-sensor 'none'; autoplay 'none';
camera 'none'; encrypted-media 'none'; fullscreen 'self'; geolocation 'none';
gyroscope 'none'; magnetometer 'none'; microphone 'none'; midi 'none'; payment 'none';
picture-in-picture 'none'; speaker 'none'; sync-xhr 'none'; usb 'none'; vr 'none';"

Smashing Magazine 에서 사용할 수 있는 것들을 찾을 수 있다. 보통은 근데 쓰지 않는 기능은 none으로 설정할 것이다.


5. referrer 값 노출시키지 않기


웹 사이트에서 다른 곳으로 이동하는 링크를 클릭하면, 가고자 하는 웹 사이트는 웹 사이트에서 마지막 위치의 URL을 referrer 헤더로 받는다. 그런데 이 URL에는 세션 토큰이나 사용자 ID와 같은 민감한 데이터가 포함될 수 도 있기 때문에 노출하면 안된다.

이걸 막기 위해선 Referrer-Policy헤더를 no-referrer로 설정한다.

"Referrer-Policy": "no-referrer"

이렇게 만드는 것이 좋긴 한데, 세상 일이 그렇든 방문 유입경로나 코드 logic에 따라 referrer를 갖고 있어야 할 때도 있다.
Scott Helme article에서 referrer 헤더를 어떻게 설정해야 하는지 알려준다.(same origin시나 https에서 http로 이동할 때 등)

6. 유저로부터 입력값을 받는 곳에 innerHTML 사용하지 말기


크로스사이트 스크립팅(XSS 공격)은 다양한 곳에서 사용되지만 그래도 가장 많이 사용되는 곳은 바로 “innerHTML”이다.

이번에 코로나 관련해서 중학생이 신천지사이트를 털은 적이 있었는데 아마도 게시판을 사용해서 XSS 공격을 하지 않았나 싶다. 유명한 공격이다.

innerHTML을 유저로부터 필터링 되지 않은 채로 설정하면 안된다. 사용자가 직접 조작할 수 있는 값(입력 필드의 텍스트, URL의 파라메터 또는 로컬 스토리지 항목)은 먼저 검사해야한다. innerHTML보다 textContent을 쓰는 것이 더 바람직하다. 만약 게시판처럼 긴 글을 작성할 수 있게 하려면 좋은 라이브러리를 사용해야 한다.

Dom-base XSS 공격을 방지하는 방법을 알려주는 이 Trusted Types specification 구글 사이트를 눈여겨 보자.


7. UI 프레임워크 사용하기


React, Vue, Angular 같은 UI 프레임워크들은 이미 좋은 보안시스템이 내장되어 있다. 그리고 XSS의 위험으로부터도 막아 줄 수 있다. 이런 프레임워크들은 XSS에 민감한 DOM API를 사용할 일을 많이 줄여준다. 그리고 뭔가 잠재적으로 위험할만한 요소이면 이름을 dangerouslySetInnerHTML 같이 지어서 front-end 개발자로 하여금 쓰기 멈칫하게 만들고 다시 생각하게 해준다.

8. 디펜던시들을 최신상태로 유지해주기


node_moduels 폴더를 보면 알겠지만 우리가 만드는 웹 어플리케이션들은 수 백개의 퍼즐로 이루어진 레고 퍼즐과 다름이 없음을 알 수 있다. 그리고 우리가 디펜던시들을 사용할 때 보안 취약점이 있는지 없는지 확인하는 것은 매우 중요하다.

디펜던시가 믿을만한 건지 아닌지를 체크할 때 검사를 미리 해보는 것이 좋다. 이런 툴은 Dependabot Snyk에서 확인 할 수 있다.

잠재적으로 취약한 점에 대해 pull-request를 요청해주고 수정사항을 더 빨리 적용하는데 도움이 된다.

node_modules는 node가 인기 있게 만드는 이유이긴 하지만, 라이언 달이 말했듯이 불필요한 모듈들이 설치가 되는 단점이 있다. 그리고 보안에 매우 취약할 수 밖에 없다. 내가 설치한 모듈이 내 파일을 수정할 수 있는 코드가 들어있을지도 모르는데 말이다. 그래서 올해 여름에 1.0버전을 출시할 deno가 궁금해진다. 이런 취약점들을 개선했다고 말하는데 아직 살펴보진 않아서 대충 흘러가는 뉴스들만 보고 있다.

9. third-party를 사용하기전에 한번 더 생각해보기


Google Analytics, Intercom, Mixpanel 같은 third-party들은 “한 줄 코드” 솔루션을 제공한다. 그런데 그 뜻은 그와 동시에 third-party가 손상되면 웹 사이트가 손상되기 때문에 웹 사이트를보다 취약하게 만들 수 있다.

third-party를 통합하기로 결정한 경우에는 해당 서비스가 정상적으로 작동하도록 허용하는 가장 강력한 CSP 정책을 설정하는 것이 좋다. 널리 사용되는 대부분의 서비스는 필요한 CSP 가이드라인이 있으니 지침을 따라야 한다.

Google Tag Manager, Segment 같이 조직 내 모든 사람이 더 많은 third-party를 통합 할 수 있는 기타 툴들을 사용할 때는 특히 주의해야 한다. 이 도구에 액세스 할 수있는 사람은 어떻게 보안이 돌아가는지 알고 있어야 한다.

10. third-party 스크립트에 Subresource integrity 사용하기


사용하는 third-party 스크립트의 경우에는 가능한 한 integrity attribute를 사용하는 것이 좋다. 브라우저 자체 기능에는 로드 중인 script들의 암호화 해시를 검증하고 변경되지 않았는지 확인할 수 있는 Subresource Integrity 가 있다.

이건 script 태그가 이렇게 생겼다.

<script
  src="https://example.com/example-framework.js"
  integrity="sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/uxy9rx7HNQlGYl1kPzQho1wx4JwY8wC"
  crossorigin="anonymous"
></script>

이러한 기법은 third-party 라이브러리에는 괜찮다. 그런데 third-party 서비스에는 그닥 유용하지 못하다. 왜냐면 대부분의 경우에는 third-party 서비스의 스크립트를 추가할 경우 그 스크립트가 하는 역할은 보통 다른 디펜던트 스크립트를 불러오는 역할을 하기 때문이다. 근데 이건 언제든지 바뀔 수 있으므로 위험한지 아닌지 무결성을 검증할 수 있는지 없는지 우리가 알 수 없다.

결론


브라우징에서 저장하는 기능들은 요즘 웹어플리케이션을 만들때 매우 중요하다. 그리고 당연히 사용하는 유저들은 개인정보가 안전하게 저장되는 것을 원할 것이다. 그리고 이 데이터는 back-end에 저장되지만, 데이터 보안에 대한 책임은 client사이드에도 역시 책임이 있다.

악의적인 사용자들이 할 수 있는 UI 공격은 매우 많지만 이 블로그에서 기술한 권고를 잘 따른다면 방어 가능성을 크게 높여줄 수 있다.

Originally published at https://konstantinlebedev.com.