2022. 2. 18. 21:58ㆍFrontend
Overview
web.dev에서 소개하는 Web Security에 대한 내용들을 2부에 걸쳐서 정리합니다. 모든 내용들을 다 다루지는 않고, 개인적으로 중요하다고 생각하는 부분들을 추려서 중점적으로 정리했습니다. 자세한 내용들은 아래 Table of Contents의 링크를 통해 확인하실 수 있습니다.
Table Of Contents
정보 유출 방지
- Browser sandbox
- Same-origin policy
- Cross-Origin Resource Sharing (CORS)
- Making your website "cross-origin isolated" using COOP and COEP
- Why you need "cross-origin isolated" for powerful features
- Protect your resources from web attacks with Fetch Metadata
XSS로부터 웹사이트 보호
- Prevent DOM-based cross-site scripting vulnerabilities with Trusted Types
- Mitigate cross-site scripting (XSS) with a strict Content Security Policy (CSP)
추적으로부터 사용자 보호
- Digging into the Privacy Sandbox
- SameSite cookies explained
- SameSite cookie recipes
- Referer and Referrer-Policy best practices
- Getting started with Trust Tokens
- Improving user privacy and developer experience with User-Agent Client Hints
- Schemeful Same-Site
- What is FLoC
-
정보 유출 방지
Browser Sandbox
A sandbox is a security mechanism used to run an application in a restricted environment. For example, JavaScript can add and modify elements on the page but might be restricted from accessing an external JSON file. This is because of a sandbox feature called same-origin
샌드박스란 애플리케이션을 제한된 환경에서 동작하도록 하는 보안 장치입니다. 이를테면 Javascript는 접근이 허용된 컨텍스트 내의 요소들을 자유롭게 변경할 수 있지만 접근이 허용되지 않은 컨텍스트의 요소는 접근하거나 변경할 수 없습니다. 브라우저 샌드박스는 이러한 보안 장치를 제공함으로써 외부 스크립트의 실행을 최대한 막으려고 하며, 이는 SameOrigin Policy(동일 출처 정책)에 그 근간을 둡니다.
Same-origin policy
저번 포스팅에서 확인했듯, Origin은 scheme + host + port로 정의됩니다. 아래에서 이야기하는 "동일 출처 정책"은 모두 이 출처의 정의를 기준으로 합니다. (Same Site와 헷갈리지 않도록 주의합니다.)
What is Permitted and What is Blocked?
Same Origin 정책은 Cross Origin 간의 리소스 공유를 막는 기본 브라우저 정책입니다. 하지만 브라우저는 서로 다른 Origin의 Iframe, CSS, Form, Images, Multimedia들과 상호작용하므로 브라우저에서 이러한 리소스들의 Embedding은 허용하며 이는 적절한 CSP(Content Security Policy) 정책 헤더를 통해 설정할 수 있습니다.
Generally, Embedding a cross-origin resource is permitted, while reading a cross-origin resource is blocked.
Iframe
- X-Frame-Options 의 적절한 설정을 통해 Embedding은 허용합니다.
- Cross-Origin Reading은 (Javascript로 Iframe안의 요소에 접근하도록 하는) 금지되어 있습니다. (즉 Iframe으로 임베드한 사이트의 요소를 Javascript로 접근하는 것은 금지)
CSS
- Cross-Origin CSS는 기본적으로 Embedding 가능합니다.(스크립트 실행이 아니기 때문)
- Content-Type : text/css로 명시해주는 것이 좋습니다. (X-Content-Type-Options를 nosniff로 설정해서 text/css 타입인 경우에는 CSS로만 취급하도록 설정해주는 것이 좋습니다)
Form, Images, Multimedia
- Form은 Cross-Origin Destination에 쓰기가 가능하고, action의 값으로 Cross-Origin URL 사용 가능합니다.
- image의 경우 Embedding은 허용하지만 Javascript 를 사용해서 Canvas에 로딩하는 건 허용하지 않습니다.
image tag에 crossorigin attribute가 설정되어 있지 않으면 브라우저는 non-CORS 요청을 보냅니다. 그리고 해당 이미지를 ‘Tainted’ 이미지로 마크하고 캔버스에서 사용되는 것을 막습니다. (보안을 위해)
If the crossorigin attribute is not specified, then a non-CORS request is sent (without the request header), and the browser marks the image as tainted and restricts access to its image data, preventing its usage in elements.
Script
- Embed 가능하지만 하지만 Script안에서 특정 APi 호출은 CORS 정책에 따라 안될 수도 있습니다.
Allowing cross-origin use of images and canvas - HTML: HyperText Markup Language | MDN
: The Image Embed element - HTML: HyperText Markup Language | MDN
Clickjacking
Clickjacking(클릭재킹)은 보안 공격기법 중 하나로 Iframe을 사용해서 이루어집니다. 사용자의 사이트에 Iframe으로 공격의 대상이 되는 사이트를 임베드한 후, 여기서 일어나는 클릭을 Transparent Button 등을 사용해서 가로채는 동작입니다. 따라서 적절한 X-Frame-Option 헤더 설정을 통해 사이트가 iframe으로 임베드 되지 못하도록 막아주어야 합니다.
An attack called "clickjacking" embeds a site in an iframe and overlays transparent buttons which link to a different destination. Users are tricked into thinking they are accessing your application while sending data to attackers.
클릭재킹 (Clickjacking, User Interface redress attack, UI redress attack, UI redressing)은 웹 사용자가 자신이 클릭하고 있다고 인지하는 것과 다른 어떤 것을 클릭하게 속이는 악의적인 기법으로써 잠재적으로 공격자는 비밀 정보를 유출시키거나 그들의 컴퓨터에 대한 제어를 획득할 수 있게 된다
https://ko.wikipedia.org/wiki/%ED%81%B4%EB%A6%AD%EC%9E%AC%ED%82%B9
Cross Origin Resource Sharing
위에서도 간단하게 설명한 것처럼 브라우저의 기본 보안 정책은 "Same-Origin"입니다. 따라서 기본적으로 서로 다른 Origin(출처)로의 요청은 Block 됩니다. 하지만 애플리케이션을 개발하다 보면, 상호 간의 약속을 통해 서로 다른 출처 간의 요청을 주고받아야 하는 경우가 있습니다. (외부 API를 사용하거나, 다른 도메인을 사용하는 사내 다른 서비스를 호출하거나) 이런 경우에는 명시적으로 추가 출처를 사용한다는 것을 명시해주어야 합니다.
CORS를 활성화하면 서버가 브라우저에 추가 출처를 사용할 수 있음을 알릴 수 있습니다.
Access-Control-Allow-Origin
서버에서 Access-Control-Allow-Origin 헤더는 해당 서버가 보낼 응답을 허용할 도메인을 확인합니다. 여기에 특정 오리진을 명시하면 해당 오리진으로부터 온 요청은 정상적인 응답을 받을 수 있고, 명시되지 않은 오리진으로부터 온 요청은 CORS Error가 나며 정상적인 응답을 받을 수 없게 됩니다. 만약 모든 오리진으로부터 온 요청을 사용하고 싶으면 특정 도메인을 명시하는 대신에 와일드카드(*)를 사용하면 되지만, 이 경우 아래에서 설명할 credentials 정보를 첨부할 수 없게 됩니다. (첨부하면 오류가 발생합니다.
For requests without credentials, the literal value "*" can be specified as a wildcard; the value tells browsers to allow requesting code from any origin to access the resource. Attempting to use the wildcard with credentials results in an error.
Access-Control-Allow-Origin: https://developer.mozilla.org
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin
Access-Control-Allow-Credentials
Access-Control-Allow-Credentials는 자격증명정보(Cookie, Authorization Header, TLS Certificate)를 받아서 브라우저가 이를 자바스크립트 코드에 노출할지를 알려주는 정보입니다. 이 Credential을 사용하기 위해서는 위에서 설명했던 Access-Control-Allow-Origin에 와일드카드(*)가 설정되어 있으면 안 되며, 아래와 같이 credential: Include로 요청해서 받은 Credentials는 자바스크립트 코드에서 접근해 사용이 가능합니다.
The Access-Control-Allow-Credentials response header tells browsers whether to expose the response to the frontend JavaScript code when the request's credentials mode (Request.credentials) is include.
fetch('<https://example.com>', {
mode: 'cors',
credentials: 'include'
})
Access-Control-Allow-Credentials: true
Preflight Requests
Preflight Request(사전요청)은 Cross Origin 요청 시에 서버 측에서 그 요청의 메서드와 헤더에 대해 인식하고 있는지를 체크하는 요청입니다. 이 요청(OPTIONS Request)은 브라우저에서 자동으로 발생되며 max-Age 헤더 값에 따라 그 기간은 다르긴 하지만 브라우저에서 Cache 되므로 매번 보내는 것도 아니고, 자바스크립트 개발자가 명시적으로 호출해야 하는 요청도 아닙니다. 사전 요청은 다음과 같은 조건을 "모두"만족하면 발생됩니다.
Condition(동시만족)
- GET, POST 또는 HEAD 이외의 메서드를 사용하는 요청
- Accept, Accept-Language 또는 Content-Language 이외의 헤더를 포함하는 요청
- application/x-www-form-urlencoded, multipart/form-data 또는 text/plain 이외에 Content-Type 헤더가 있는 요청
"preflighted" request는 위에서 논의한 “simple requests” 와는 달리, 먼저 OPTIONS 메서드를 통해 다른 도메인의 리소스로 HTTP 요청을 보내 실제 요청이 전송하기에 안전한지 확인합니다. cross-origin 요청은 유저 데이터에 영향을 줄 수 있기 때문에 이와 같이 미리 전송(preflighted)합니다.
Example
# Request
OPTIONS /resource/foo
Access-Control-Request-Method: DELETE
Access-Control-Request-Headers: origin, x-requested-with
Origin: https://foo.bar.org
# Response
HTTP/1.1 204 No Content
Connection: keep-alive
Access-Control-Allow-Origin: https://foo.bar.org
Access-Control-Allow-Methods: POST, GET, OPTIONS, DELETE
Access-Control-Max-Age: 86400
https://developer.mozilla.org/en-US/docs/Glossary/Preflight_request
COOP & COEP → Cross Origin Isolated
Cross Origin Isolated
Cross Origin Isolated(교차출처 간 격리 상태)란 Cross Origin 문서에 의해 로드되도록 선택하지 않은 리소스나 Iframe의 로드를 차단하여 통신이 기본적으로 Same-Origin 간에서만 이루어지도록 하는 정책입니다. 즉, Cross Origin 창이 문서와 직접 상호작용하는 것을 방지하는 것입니다. 이는 아래에서 설명할 "브라우징 컨텍스트"의 격리를 통해 이루어지고, 실제로 이를 사용하기 위해서는 아래처럼 COEP, COOP 등을 명시적으로 설정해주어야 합니다.
Cross-Origin-Embedder-Policy: require-corp
Cross-Origin-Opener-Policy: same-origin
COOP 및 COEP 통합
- Cross-Origin-Opener-Policy: same-origin 최상위 문서에 설정
- 동일한 출처를 가진 창과, 해당 문서에서 열린 창은 동일한 출처(Same Origin)에 동일한 COOP 설정을 갖지 않는 한 별도의 탐색 컨텍스트 그룹을 갖게 됩니다.
- 동일한 탐색 컨텍스트 그룹(브라우징 컨텍스트)을 갖게 된다는 것은 서로 같은 Document를 참조한다는 의미이므로 두 창이 별도의 탐색 컨텍스트 그룹을 갖게 된다는 것은 두 창간의 상호 통신이 비활성화된다는 것을 의미합니다.
- 리소스에 CORP 또는 CORS 가 활성화되어 있는지 확인
- CORP → Cross Origin Resource Policy
- CORS → Cross Origin Resource Sharing
Why you need Cross-Origin Isolated?
Context
- 웹은 Same-Origin Policy 를 기반으로 보안 모델이 구축되어 있습니다. (Origin: Hostname + Port)
- 이 Same-Origin 정책에는 바로 아래에서 소개할 역사적인 예외가 있었고 이는 느슨한 동일 출처 정책을 만드는 원인으로 XSS, Clickjacking 등 여러 보안 부작용을 만들어냈습니다.
- 출처 간 iframe 삽입
- 이미지 또는 스크립트와 같은 출처 간 리소스 포함
- DOM 참조로 출처 간 팝업 창 열기
- 따라서 이 역사적인 예외에 대한 보안 패치를 위해 CORS라는 새로운 프로토콜을 도입했고, 이는 원본 간 리소스에 대한 직접 스크립트 액세스를 암시적으로 제거하는 것을 의미합니다.
- 즉, 리소스의 단순 Embed 까지는 어느 정도 허용하더라도(기존 정책때문에) Embed 된 내용들에 대해서 직접 조작하는 건 CORS 없이는 허용하지 않겠다 (암시적으로 제거)라는 의미입니다.
Browsing Context Group
브라우징 컨텍스트(브라우징 맥락)는 브라우저가 Document를 표시하는 환경을 의미합니다. 보통은 브라우징 컨텍스트를 구분하는 기준은 “탭(Tab)"이지만 설정에 따라서 브라우저 창이나 페이지 내의 프레임도 가능하며, 각 브라우징 컨텍스트는 특정 출처, 활성화된 문서의 출처, 그리고 표시했던 모든 문서의 방문 기록을 갖는다. (HTML Browsing Context Spec HTML)
Cross Origin Embedder Policy (출처 간 임베더 정책)
COEP의 역할은 페이지에서 로드된 교차 출처 리소스가 "명시적으로" CORS나 CORP를 사용해서 로드된 것임을 "보증"해주는 것입니다. 이 response헤더는 Document를 로드할 때 적용되며, 이 정책이 적용(require-corp)된 Document에서는 무조건 Same-Origin의 리소스만 로드하거나, 명시적으로 CORS, CORP를 통해 허용된 리소스만 요청하는 것입니다. 즉 COEP가 require-corp로 설정되어 있다면, 이 문서는 사전에 약속되지 않은 Cross Origin 리소스는 로드하지 않는다는 것을 보장할 수 있습니다.
A document can only load resources from the same origin, or resources explicitly marked as loadable from another origin. If a cross origin resource supports CORS, the crossorigin attribute or the Cross-Origin-Resource-Policy header must be used to load it without being blocked by COEP.
Cross-Origin-Embedder-Policy: require-corp
The COEP header allows you to make sure that any cross-origin resources loaded by your page are explicitly permitted to be loaded with either CORS or CORP, or they will be blocked from loading.
Cross-Origin-Embedder-Policy - HTTP | MDN
Cross Origin Resource Sharing (출처 간 리소스 공유)
출처간 리소스가 CORS를 지원하는 경우 crossorigin 속성을 사용하면 COEP에 의해 차단되지 않고 리소스를 로드할 수 있습니다. 즉 서버 리소스가 CORS를 지원하고 COEP가 require-corp로 설정되어 있는 경우, COEP에 의해 차단되지 않고 리소스를 로드합니다.
Cross Origin Resource Policy (출처 간 리소스 정책)
COEP의 컨텍스트에서 CORP는 리소스를 로드할 수 있는 사람에 대한 리소스 소유자의 정책을 지정할 수 있습니다.
- COEP는 Embedding Policy. 즉 출처 간 리소스를 문서에서 로드할 수 있는지 없는지를 제한하는 정책입니다.
- COEP를 require-corp 로 설정하면, CORP를 cross-origin으로 표시한 리소스들, 즉 리소스 소유자가 의도적으로 crossorigin으로 지정한 리소스에 대해서만 브라우저가 로드하도록 허용합니다.
- CORP는 cross-origin 이외에도 same-site, same-origin 값을 설정할 수 있습니다.
Cross Origin Opener Policy (출처 간 오프너 정책)
이 정책을 설정하면 최상위 창이 다른 최상위 창과 직접 상호작용할 수 없도록 다른 문서를 다른 탐색 컨텍스트 그룹에 배치합니다.
COEP COOP CORP CORS CORB - CRAP that's a lot of new stuff!
https://snigel.com/blog/a-simple-guide-to-coop-coep-corp-and-cors
추적으로부터 사용자 보호
SameSite Cookies Explained
자사 쿠키 및 타사 쿠키
예를 들어, 사이트에 YouTube 동영상을 삽입하면 방문자의 플레이어에 "나중에 보기" 옵션이 표시됩니다. 방문자가 이미 YouTube에 로그인되어 있는 경우 해당 세션은 타사 쿠키에 의해 내장 플레이어에서 제공되고 있는 것입니다. 즉, "나중에 보기" 버튼을 누르면 로그인하거나 페이지에서 다른 곳으로 이동했다가 다시 YouTube로 이동할 필요 없이 한 번에 동영상을 저장
예를 들어, evil.example를 방문한다면 your-blog.example에 대한 요청이 트리거 될 수 있고 브라우저는 기꺼이 관련 쿠키를 첨부할 것입니다. 여러분의 블로그가 이러한 요청을 검증하는 방식에 주의를 기울이지 않는다면 evil.example이 게시물을 삭제하거나 자체 콘텐츠를 추가하는 등의 작업을 트리거할 수 있습니다.
위의 예시에서 확인할 수 있는 쿠키의 속성은 다음과 같습니다.
- Cookie는 동일한 호스트 사이에서만 전송된다. (a.com의 쿠키가 b.com으로 전송되지는 않음)
- 특정 문서(a.com)에서 다른 사이트(b.com)의 리소스를 요청할때 (image embed 등) 별도의 설정이 없으면 b.com으로 요청을 보낼 때 브라우저가 이미 가지고 있는 b.com의 쿠키가 같이 전송된다.
이러한 쿠키 전송방식이 도움이 될 때도 있지만, CSRF 공격에 사용되기도 합니다. 따라서 쿠키와 관련해서 자신의 의도(이 경우에는 보낼 것이고 이 경우에는 보내지 않을 것이다)를 정확하게 표현할 방법이 필요하고 Cookie의 SameSite 속성을 사용하면 이를 해결할 수 있습니다 (자세한 내용은 아래 링크를 참조하세요)
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite
SameSite Rules (Reminder)
- SameSite 특성이 없는 쿠키는 SameSite=Lax로 처리됩니다.
- SameSite=None을 포함한 쿠키는 Secure도 지정해야 합니다. 즉, 보안 컨텍스트(HTTPS)가 필요합니다.
'Frontend' 카테고리의 다른 글
[Web.dev] Accessibility (1) (0) | 2022.03.06 |
---|---|
[Web.dev] Chrome DevTools (0) | 2022.02.28 |
[Web.dev] Web Security (1) (0) | 2022.02.10 |
React Deep Dive - React Event System (2) (0) | 2021.12.31 |
[React] Atomic Design Pattern에 대한 고찰 (0) | 2021.09.18 |