CORS - Browser Security Model

πŸ’„λ“€μ–΄κ°€λ©°

μ €λ²ˆ μ£Ό λΆ€ν„° 자꾸 cors cors 이야기가 λ‚˜μ™€μ„œ κ·Έλƒ₯ λŒ€κ°• μ΄ν•΄ν•˜κ³  λ„˜μ–΄κ°”λŠ”λ° μ•„μ£Ό 상 μ°μ°ν•œ λŠλ‚Œμ΄ λ“€μ–΄ 였늘 λ‚˜λ¦„ ν•™μŠ΅μ„ λ‹€μ‹œ 해보고 λΈ”λ‘œκΉ…μ„ ν•œλ‹€.

일단 CORS λŠ” Cross-Origin-Resource-Sharing 의 μ•½μžμ΄λ‹€.

μ„œλ‘œ λ‹€λ₯Έ origin 간에 λ¦¬μ†ŒμŠ€λ₯Ό κ³΅μœ ν•˜κ²Œ ν•˜λŠ” 방식이며 좔가적인 HTTP 헀더λ₯Ό μ΄μš©ν•΄μ„œ,

λΈŒλΌμš°μ €κ°€ 자발적으둜 이 λΈŒλΌμš°μ €μ˜ μ–΄ν”Œλ¦¬μΌ€μ΄μ…˜μ„ μ‚¬μš©ν•˜λŠ” μœ μ €λ“€μ„ λ³΄ν˜Έν•˜κΈ° μœ„ν•œ μ •μ±… 이라고 ν•œλ‹€.

λΈŒλΌμš°μ € 만의 자발적인 λ³΄μ•ˆ 쑰치.

근데 μ΄λ ‡κ²Œ 적고 끝낼리가 μ—†λ‹€.

μ§€κΈˆ

맀우

맀우

μ°μ°ν•˜λ‹€.

πŸ‘©πŸ»β€πŸ¦³CORS 의 λ“±μž₯ 이유 (1) 리즈 μ‹œμ ˆ

μ˜›λ‚  리즈 μ‹œμ ˆ μ—λŠ” μ„œλ²„μ—μ„œ 내렀받은 client 둜만 톡신을 ν–ˆλ‹€κ³  ν•œλ‹€.

λ‹€μˆ˜μ˜ μ‚¬μš©μžκ°€ μ„œλ²„μ—μ„œ λ‚΄λ €μ€€ 자체 ν΄λΌμ΄μ–ΈνŠΈλ‘œλ§Œ 톡신을 ν–ˆλ‹€.

이 λ•Œ μ„œλ²„ μž…μž₯μ—μ„œλŠ” ν΄λΌμ΄μ–ΈνŠΈκ°€ μœ„ν•΄ λ‚˜ μ˜μ‹¬μ˜ 여지가 μ—†λ‹€. Same - Origin 이닀.

μ„œλ²„ λ‚΄ 자체 ν΄λΌμ΄μ–ΈνŠΈλ₯Ό 가지고 ν†΅μ‹ ν•˜κΈ°μ— κ°œλ³„ ν΄λΌμ΄μ–ΈνŠΈμ˜ μš”μ²­μ„ μ„œλ²„ μž…μž₯μ—μ„œ 막을 ν•„μš”κ°€ μ—†μ—ˆλ‹€.

πŸ‘©πŸ»β€πŸ¦³CORS 의 λ“±μž₯ 이유 (2) SPA 의 λ“±μž₯

Single Page Application 의 λ“±μž₯으둜 μ—¬λŸ¬ 곳에 μžˆλŠ” μžμ› (resourse) 을 ν™œμš©ν•  ν•„μš”κ°€ 생겼닀.

자체 μ„œλ²„μ˜ ν΄λΌμ΄μ–ΈνŠΈ 뿐만 μ•„λ‹ˆλΌ 타 ν”Œλž«νΌμ˜ API λ₯Ό μ“°κ²Œ 될 일이 λ§Žμ•„μ‘Œλ‹€.

μ§€κΈˆ 이 λΈ”λ‘œκ·Έ 만 ν•˜λ”λΌλ„ 컀피 ν•œμž” κ°’μ˜ κΈ°λΆ€λ‚˜ facebook 이동 λ“±μ˜ λ¦¬μ†ŒμŠ€ 등을 자유자재둜 λ‚΄μž₯μ‹œν‚¬ 수 μžˆμ§€ μ•Šμ€κ°€?

κ·Έλž˜μ„œ μ΄μ œλŠ” Same Origin 이 μ•„λ‹ˆλΌ Cross Origin μš”μ²­μ„ ν•΄μ•Ό ν•œλ‹€.

πŸ‘±πŸΌβ€β™€οΈCross Origin μš”μ²­ ν—ˆμš©μ˜ 계기

κ·Έλž˜μ„œ 이제 Cross origin μ—μ„œ λ¦¬μ†ŒμŠ€ (μ„œλ²„μžμ›) λ₯Ό μš”μ²­ν•˜μ—¬ μ‚¬μš©ν•˜κ²Œ λ˜λŠ”λ° (ꡬ글 μ§€λ©”μΌλ‘œ κΉƒν—ˆλΈŒ κ°€μž…ν•˜λŠ” λ“±μ˜)..

λ³΄μ•ˆμƒμ˜ 이유둜 λΈŒλΌμš°μ €μ—μ„œμ˜ 크둜슀 도메인 μš”μ²­μ€ 기본적으둜 μ œν•œλ˜μ–΄ μžˆλ‹€κ³  ν•œλ‹€.

μ΄μœ λŠ” λ§Œμ•½ μ„œλ²„κ°€ μš”μ²­μ„ μ—΄μ–΄ λ†“μœΌλ©΄ ν΄λΌμ΄μ–ΈνŠΈκ°€ μ„œλ²„μ— μ–΄λ– ν•œ λ¦¬μ†ŒμŠ€λ₯Ό 생성할지 확인을 ν•  수 μ—†μ–΄μ„œ 라고 ν•œλ‹€.

그런데 κ°œλ°œμžλ“€μ΄ λΈŒλΌμš°μ € 벀더사듀에 β€œμ›Ή app 고도화” λ₯Ό μœ„ν•΄ κ°œμ„  μš”μ²­μ„ ν–ˆλ‹€κ³  ν•œλ‹€.

κ·Έλž˜μ„œ μ΄μ œλŠ” β€œμ„œλ²„κ°€ Allow ν•œ λ²”μœ„β€ λ‚΄μ—μ„œ cross origin μš”μ²­μ„ ν—ˆμš©ν•˜κ²Œ λ˜μ—ˆλ‹€κ³  ν•œλ‹€.

고둜 이제 λͺ¨λ“  λ„λ©”μΈμ—μ„œ ν•΄λ‹Ή μ„œλ²„λ‘œ CORS μš”μ²­μ„ ν•  수 있게 λ˜μ—ˆλ‹€. (ν—ˆμš© μ—¬λΆ€λŠ” ν•΄λ‹Ή μ„œλ²„ λœ»λŒ€λ‘œ)

μ•„λž˜λŠ” μ„œλ²„ μž…μž₯μ—μ„œ 바라본 ν—ˆμš© μš”κ±΄μ΄λ‹€.

const defaultCorsHeaders = {
  'access-control-allow-origin': '*', // λͺ¨λ“  도메인을 ν—ˆμš© (μ™€μΌλ“œμΉ΄λ“œ)
  'access-control-allow-methods': 'GET, POST, PUT, DELETE, OPTIONS', // ν—ˆμš©κ°€λŠ₯ν•œ λ©”μ†Œλ“œλ“€
  'access-control-allow-headers': 'content-type, accept', // Header 에 μ“Έ 수 μžˆλŠ” νƒ€μž…
  'access-control-max-age': 10, // Preflight request λŠ” 10μ΄ˆκΉŒμ§€ ν—ˆμš© λœλ‹€.
}

μ΄λŸ¬ν•œ 쑰건듀을 κ°–μΆ˜ request 듀은 크둜슀 λ„λ©”μΈμ—μ„œ λ¦¬μ†ŒμŠ€ μš”μ²­μ΄ κ°€λŠ₯ν•˜λ„λ‘ ν—ˆμš©ν•΄ μ£Όκ² λ‹€ λΌλŠ” μ„€μ • μ½”λ“œ 이닀.

πŸ‘±πŸΌβ€β™€οΈCORS ν•΄λΆ€ν•˜κΈ°

Same-Origin requests, Always allowed.

domain-a.com ν΄λΌμ΄μ–ΈνŠΈκ°€ 동일 domain-a.com λ‚΄ Web Server 에 μ•Ό λ‚˜ 웃지 λΆˆλŸ¬μ™€μ•ΌλΌ ν•˜λ©΄μ„œ λ¦¬μ†ŒμŠ€ μš”μ²­μ„ (GET Request) λ„£λŠ”λ‹€.

같은 도메인이라 λ¬Έμ œκ°€ λ˜μ§€ μ•ŠλŠ”λ‹€.(Same-Origin requests, Always allowed.)

μ„œλ²„κ°€ 옹 그래 κ°€μ Έκ°€λ ΄~~ μ—¬κΈ° μ˜›λ‹€ 웃지~

Cross-Origin requests, Controlled by CORS.

domain-a.com ν΄λΌμ΄μ–ΈνŠΈ λŠ” 뢈만이 λ§Žλ‹€.

λ‚΄ 자체 μ„œλ²„μ— μžˆλŠ” 웃지은 이제 μ‹œλŒ€κ°€ ν•œμ°Έ 지났단 말이닀.

κ·Έλž˜μ„œ μš”μƒˆ νŠΈλ Œλ“œμ— λ§žμΆ°μ§„ 타 웃지 Web Server에 (domain-b.com)

λΉ„ μ •ν˜•ν™” 된 웃지 이미지λ₯Ό μš”μ²­ν•˜κΈ°μ— 이λ₯Έλ‹€.

이걸 쀘 말어?

μœ„μ˜ 이미지가 domain-b.com μ—μ„œ 뢈렀질 μˆ˜λ„ 있고 아닐 μˆ˜λ„ μžˆλ‹€.

μ§€κΈˆ 상황은 domain-a.com μ—μ„œ 타 도메인 μ„œλ²„μΈ domain-b.com 에 이미지λ₯Ό μš”μ²­ν•˜λŠ” Cross-Origin requests 이닀.

μ΄λŠ” CORS 에 μ˜ν•΄ 컨트둀 λœλ‹€.

πŸ‘±πŸΌβ€β™€οΈκ΅μ°¨ 좜처 λ¦¬μ†ŒμŠ€ κ³΅μœ κ°€ λ™μž‘ν•˜λŠ” 방식을 λ³΄μ—¬μ£ΌλŠ” μ„Έ 가지 μ‹œλ‚˜λ¦¬μ˜€

https://developer.mozilla.org/ko/docs/Web/HTTP/CORS

1. Simple Requests

  • κ°€λŠ₯ν•œ Methods : GET, HEAD, POST
  • μžλ™μœΌλ‘œ μ„ΈνŒ…λ˜λŠ” Header 둜만 simple requests κ°€ κ°€λŠ₯.
  • 맀뉴얼 헀더 (기본적 ν—€λ”μ—μ„œ μΆ”κ°€λ˜λŠ” 헀더) λŠ” simple requests λΆˆκ°€λŠ₯.
  • Content-Type ν—€λ”λŠ” λ‹€μŒμ˜ κ°’λ“€λ§Œ ν—ˆμš©λœλ‹€.
application / x - www - form - urlencoded // application/json μ΄λŸ°κ±°λŠ” λΆˆκ°€λŠ₯.
multipart / form - data
text / plain

예둜, ν΄λΌμ΄μ–ΈνŠΈμ—μ„œ 타 도메인에 자료λ₯Ό μš”μ²­ν•œλ‹€.

GET /resources/public-data/ HTTP/1.1
Host: bar.other
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:71.0) Gecko/20100101 Firefox/71.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Connection: keep-alive
Origin: https://foo.example

타 도메인 μ„œλ²„μ—μ„œλŠ” μš”μ²­μ„ λœ―μ–΄λ³Έ λ’€,

GET μš”μ²­β€¦ 삐빅..

메뉴얼 헀더.. μ•„λ‹ˆλ„€ 삐빅..

Content-Type 헀더.. ν•΄λ‹Ή 사항에 ν•΄λ‹Ή.. 삐빅..

μ•„! 이 ν΄λΌμ΄μ–ΈνŠΈμ˜ μš”μ²­μ€ Simple Requests 에 ν•΄λ‹Ήν•˜λŠ” κ΅¬λ‚˜!

μ„œλ²„λŠ” 200OK λ₯Ό λ‚ λ¦°λ‹€. 응닡 μ½”λ“œλ₯Ό μžμ„Ένžˆ ν™•μΈν•΄λ³΄μž!

HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 00:23:53 GMT
Server: Apache/2
Access-Control-Allow-Origin: *
// μ• μŠ€ν„°λ¦¬μŠ€ν¬ (*) λŠ” λͺ¨λ“  λ„λ©”μΈμ—μ„œ 접근이 κ°€λŠ₯함을 μ˜λ―Έν•œλ‹€!
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Transfer-Encoding: chunked
Content-Type: application/xml

[…XML Data…]

2. Preflighted Requests

simple request μ™€λŠ” λ³„κ°œλ‘œ Preflighted Requests λŠ” β€œOPTIONS” λΌλŠ” λ©”μ†Œλ“œμ— μ˜ν•΄ μš”μ²­μ΄ 이루어 진닀.

그게 뭐?

크둜슀 도메인에 POST μš”μ²­μ„ 날렸을 뿐인데 POST 보닀 λ¨Όμ € λΈŒλΌμš°μ €μ—μ„œ μžλ™μœΌλ‘œ

β€œOPTIONS Request” λΌλŠ” Preflighted Requests κ°€ λ³΄λ‚΄μ§€λŠ” 것이닀.

const def = {
  Preflighted: '이전에 λ¨Όμ € λ‚ μ•„κ°„λ‹€',
}

즉, POST μš”μ²­λ³΄λ‹€ λ¨Όμ € λ‚ μ•„κ°€μ„œ (OPTIONS) λ‚˜ μ ‘κ·Ό κ°€λŠ₯ν•΄ μ€Όμš°? ν•˜λ©΄μ„œ 간을 λ³΄λŠ” 것이닀.

2-1. Preflighted 의 트리거(λ°œμƒ) λ˜λŠ” 쑰건

  • μš”μ²­ κ°€λŠ₯ν•œ Methods : PUT, DELETE, CONNECT, OPTIONS, TRACE, PATCH
  • μš”μ²­λ“€μ— λ‹€λ₯Έ 헀더듀을 ν¬ν•¨ν•˜λŠ” κ²½μš°μ—λ„ Preflighted request κ°€ 싀행됨
  • Content-Type 헀더에 λ‹€μŒμ˜ 값이 μ•„λ‹Œ 것듀이 λ“€μ–΄κ°€λ©΄ Preflighted λ°œλ™
application / x - www - form - urlencoded
multipart / form - data
text / plain

μ•„λ‹Œ 것듀은 application / json 이 μžˆκ² μ§€. μ–˜λŠ” ν”„λ¦¬ν”ŒλΌμ΄νŠΈλ‘œ λ°œλ™λ˜λŠ”κ±°λ‹€.

POST μš”μ²­ 보내면 λ¨Όμ € OPTIONS κ°€ λ‚ λΌκ°€λŠ”κ±°μ•Ό μ„œλ²„λ‘œ.

λ§ˆμ§€λ§‰μœΌλ‘œ 기본으둜 μ„ΈνŒ…λ˜λŠ” 헀더가 μ•„λ‹Œ λ‚΄κ°€ μ›ν•˜λŠ” λŒ€λ‘œ μ»€μŠ€ν„°λ§ˆμ΄μ§• ν•œ 헀더λ₯Ό μ„ΈνŒ…ν•˜λŠ” κ²½μš°λ„

Preflighted Requests κ°€ λ°œλ™ λœλ‹€.

2-2. Preflighted Requests λ°œλ™ λ©”μ»€λ‹ˆμ¦˜

πŸ‘©πŸΏβ€πŸ¦°Client : foo.example μ—μ„œ μš”μ²­μ„ λ‚ λ¦°λ‹€. μ•Όμ•„ server.b.com μ•„ λ‚˜ ν”„λ‘œν•„μ— μƒˆλ‘œ 웃지쒀 μΆ”κ°€ν•΄ 보자?

πŸ–₯Server : 뭐여 μ‹œλ°©. λ‹ˆ μš”μ²­ Simple Request.. μ•„λ‹ˆλ„€? Preflight request 보내라!

πŸ‘©πŸΏβ€πŸ¦°Client : … OPTIONS λ¦¬ν€˜μŠ€νŠΈ κ°€μš”~~ μ‚¬λž‘μ˜ λ¦¬ν€˜μŠ€νŠΈ~~

πŸ‘©πŸΏβ€πŸ¦°Client : λ―Έμ•ˆ.. λ‚˜ 이러 μ΄λŸ¬ν•œ λ°©μ‹μœΌλ‘œ POST μš”μ²­μ„ λ‚ λ¦¬λŠ”λ° 이거 λ‹ˆλ„€ μ„œλ²„μ—μ„œ ν—ˆμš©ν•˜κ³  μžˆμ§€?

πŸ–₯Server : …

πŸ‘©πŸΏβ€πŸ¦°Client : λ˜‘λ˜‘β€¦ μƒˆλ‘œλ‚˜μ˜¨ μ •μœΌλ‹ˆ 웃지 λ‚΄ ν”„λ‘œν•„μ— μ €μž₯μ’€ ν•΄λ³΄μž~

πŸ–₯Server : Access-Control-Allow-Origin 에 foo.example 이 λ“±λ‘λ˜μ–΄ μžˆλ„€.. 도μž₯ μΎ…μΎ…..

πŸ–₯Server : 200OK… ν•˜κ³  싢은 μš”μ²­ 넣어라.. λ¦¬μ†ŒμŠ€ μš”μ²­ κ°€λŠ₯ν•˜λ‹€κ³  OPTIONS 에 νƒœμ›Œ 보낸닀.

πŸ‘©πŸΏβ€πŸ¦°Client : 였 γ…Žγ…‚ 땑큐 이제 λ‚΄κ°€ μ§„μ§œ μ›ν•˜λŠ” μš”μ²­μ„ λ‚ λ¦°λ‹€. POST Request (text.xml λ“±λ“±)..

πŸ–₯Server : 200OK… 울 땑땑이 ν•˜κ³  μ‹œν”ˆλŒ€λ‘œ ν•΄~~

λ‚˜λŠ” POST λ₯Ό 날렸을 뿐인데 μžλ™μœΌλ‘œ OPTIONS Requests λΌλŠ” Preflighted Requests κ°€ λ‚ μ•„κ°„λ‹€.

μ„œλ²„μ˜ ν™”μ΄νŠΈλ¦¬μŠ€νŠΈμ— μ—†μœΌλ©΄ μ—λŸ¬ μ½”λ“œκ°€ λ‹΄κ²¨μ Έμ„œ 응닡이 올 것이닀.

3. Requests with Credentials

μ„œλ²„μ—μ„œ ν΄λΌμ΄μ–ΈνŠΈ (λΈŒλΌμš°μ €) 에 μΏ ν‚€λ₯Ό 심을 λ•Œκ°€ μžˆλ‹€. μ•ˆμ— μ„Έμ…˜ 토큰을 넣을 λ•Œκ°€ μžˆλ‹€λŠ”λ°..

유튜브 μ•Œκ³ λ¦¬μ¦˜ μžλ™ μΆ”μ²œμ΄λΌλ“ μ§€ μΏ ν‚€ λ™μ˜λ‚˜ 검색어 μ €μž₯ λ“±μ˜ κ·ΈλŸ¬ν•œ μžˆλŠ” 정보듀을 같이 보낼것인가 의 request 라 ν•œλ‹€.


Written by@[DotoriMook]
ν”„λ‘ νŠΈμ—”λ“œ μ£Όλ‹ˆμ–΄ 개발자 λ„ν† λ¦¬λ¬΅μ˜ 기술개발 λΈ”λ‘œκ·Έ μž…λ‹ˆλ‹€.

GitHubMediumTwitterFacebook