Posts ๐Ÿ’ป HTTP #4: ๊ต์ฐจ ์ถœ์ฒ˜ ๋ฆฌ์†Œ์Šค ๊ณต์œ (CORS)
Post
Cancel

๐Ÿ’ป HTTP #4: ๊ต์ฐจ ์ถœ์ฒ˜ ๋ฆฌ์†Œ์Šค ๊ณต์œ (CORS)

๊ต์ฐจ ์ถœ์ฒ˜ ๋ฆฌ์†Œ์Šค ๊ณต์œ  (CORS)

cors

๋“ฑ์žฅ ๋ฐฐ๊ฒฝ

๋™์ผ ์ถœ์ฒ˜ ์ •์ฑ… (SOP: Same-Origin Policy)

์›๋ž˜ ๋„๋ฉ”์ธ์ด ๋‹ค๋ฅด๋ฉด ์š”์ฒญ์„ ์ฃผ๊ณ ๋ฐ›์„ ์ˆ˜ ์—†๊ฒŒ ํ•˜๋ ค๋Š” ๊ฒŒ ์›น๋ธŒ๋ผ์šฐ์ €์˜ ์ฃผ์š” ์ •์ฑ…์ด์—ˆ๋‹ค. ๋™์ผ์ถœ์ฒ˜์ •์ฑ…์€ ์–ด๋–ค ์ถœ์ฒ˜(origin)์—์„œ ๋ถˆ๋Ÿฌ์˜จ ๋ฌธ์„œ๋‚˜ ์Šคํฌ๋ฆฝํŠธ๊ฐ€ ๋‹ค๋ฅธ ์ถœ์ฒ˜์—์„œ ๊ฐ€์ ธ์˜จ ๋ฆฌ์†Œ์Šค์™€ ์ƒํ˜ธ์ž‘์šฉํ•˜๋Š” ๊ฒƒ์„ ์ œํ•œํ•˜๋Š” ์ค‘์š”ํ•œ ๋ณด์•ˆ ๋ฐฉ์‹์ด๋‹ค, ์ถœ์ฒ˜๊ฐ€ ๊ฐ™๋‹ค๋Š” ๊ฒƒ์€ ๋‘ URL์˜ ํ”„๋กœํ† ์ฝœ, ํ˜ธ์ŠคํŠธ, ํฌํŠธ ์„ธ ๊ฐœ๊ฐ€ ๊ฐ™๋‹ค๋Š” ๊ฒƒ์„ ๋งํ•œ๋‹ค.

ํฌ๋กœ์Šค ๋„๋ฉ”์ธ (Cross Domain) ์ด์Šˆ

  • ๊ทธ๋Ÿฌ๋‚˜ ์ ์  ์›น์‚ฌ์ดํŠธ์—์„œ ํ•  ์ˆ˜ ์žˆ๋Š” ์ผ์ด ๋งŽ์•„์ง€๋ฉด์„œ ๋ฌธ์ œ๊ฐ€ ์ƒ๊ฒผ๋‹ค. Ajax๊ฐ€ ๋‚˜์˜ค๊ณ  Open API๊ฐ€ ํ™œ๋ฐœํ•ด์ง€๋ฉด์„œ ์›น์ด ๋‹จ์ˆœ ๋ฌธ์„œ๊ฐ€ ์•„๋‹ˆ๋ผ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ๋˜์—ˆ๋‹ค. ์ด๋•Œ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ(XMLHttpRequest)๋กœ ๋‹ค๋ฅธ ์›นํŽ˜์ด์ง€์— ์ ‘๊ทผํ•  ๋•Œ ๋‹ค๋ฅธ ์ถœ์ฒ˜์˜ ํŽ˜์ด์ง€์—๋„ ์ ‘๊ทผํ•ด์•ผ ํ•  ํ•„์š”์„ฑ์ด ์ƒ๊ฒผ๋‹ค.

  • ์ฆ‰, SOP๋ฅผ ์šฐํšŒํ•ด์„œ ์„œ๋กœ ๋‹ค๋ฅธ ๋„๋ฉ”์ธ ๊ฐ„์— ํ†ต์‹ ์„ ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ค„ ๋ฌด์–ธ๊ฐ€๊ฐ€ ํ•„์š”ํ•˜๊ฒŒ ๋˜์—ˆ๋‹ค. ๋”ฐ๋ผ์„œ JSONP, Reverse Proxy, Flash Socket์™€ ์šฐํšŒ ๋ฐฉ๋ฒ•์ด ์ƒ๊ฒจ๋‚˜๊ฒŒ ๋˜์—ˆ๋‹ค.

CORS์˜ ๋“ฑ์žฅ

  • ์›น๋ธŒ๋ผ์šฐ์ € ์ž…์žฅ์—์„œ๋Š” ์ด ์šฐํšŒ๋กœ๋ฅผ ๊ทธ๋ƒฅ ๋‘๊ธฐ์—๋Š” ๋ณด์•ˆ์ƒ์˜ ๋ฌธ์ œ๊ฐ€ ์ปค์ง€๊ณ , ๋ง‰์ž๋‹ˆ CORS์— ๋Œ€ํ•œ ์ˆ˜์š”๊ฐ€ ๋„ˆ๋ฌด ๋งŽ์•˜๋‹ค. ๋”ฐ๋ผ์„œ ํฌ๋กœ์Šค ๋„๋ฉ”์ธ ์ด์Šˆ๋ฅผ ํ•ด๊ฒฐํ•  ํ‘œ์ค€์˜ ํ•„์š”์„ฑ์ด ๋Œ€๋‘๋˜์—ˆ๋‹ค.

  • ์ด์— W3C์—์„œ ๊ถŒ์žฅ์‚ฌํ•ญ์œผ๋กœ CORS ์‚ฌ์–‘์„ ๋ฐœํ‘œํ•˜๊ฒŒ ๋˜์—ˆ๋‹ค.
  • ํ˜„์žฌ ํ™œ๋ฐœํ•˜๊ฒŒ ์œ ์ง€ ๊ด€๋ฆฌ๋˜๋Š” ์‚ฌ์–‘์€ WHATWG์˜ Fetch Living Standard

CORS(Cross-Origin Resource Sharing)๋ž€?

cors

  • ์ถ”๊ฐ€ HTTP ํ—ค๋”๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํ•œ ์ถœ์ฒ˜์—์„œ ์‹คํ–‰ ์ค‘์ธ ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ๋‹ค๋ฅธ ์ถœ์ฒ˜์˜ ์„ ํƒํ•œ ์ž์›์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋Š” ๊ถŒํ•œ์„ ๋ถ€์—ฌํ•˜๋„๋ก ๋ธŒ๋ผ์šฐ์ €์— ์•Œ๋ ค ์ฃผ๋Š” ์ฒด์ œ์ด๋‹ค.
  • ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์€ ๋ฆฌ์†Œ์Šค๊ฐ€ ์ž์‹ ์˜ ์ถœ์ฒ˜(๋„๋ฉ”์ธ, ํ”„๋กœํ† ์ฝœ, ํฌํŠธ)์™€ ๋‹ค๋ฅผ ๋•Œ ๊ต์ฐจ์ถœ์ฒ˜ HTTP ์š”์ฒญ์„ ์‹คํ–‰ํ•œ๋‹ค.
  • CORS ์ฒด์ œ๋Š” ๋ธŒ๋ผ์šฐ์ €์™€ ์„œ๋ฒ„ ๊ฐ„ ์•ˆ์ „ํ•œ ๊ต์ฐจ ์ถœ์ฒ˜ ์š”์ฒญ ๋ฐ ๋ฐ์ดํ„ฐ ์ „์†ก์„ ์ง€์›ํ•œ๋‹ค.
  • SOP๋ฅผ ๋”ฐ๋ฅด๋Š” XMLHttpRequest์™€ Fetch API๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ๋‹ค๋ฅธ ์ถœ์ฒ˜์˜ ๋ฆฌ์†Œ์Šค๋ฅผ ๋ถˆ๋Ÿฌ์˜ค๋ ค๋ฉด ๊ทธ ์ถœ์ฒ˜์—์„œ ์˜ฌ๋ฐ”๋ฅธ CORS ํ—ค๋”๋ฅผ ํฌํ•จํ•œ ์‘๋‹ต์„ ๋ฆฌํ„ดํ•ด์•ผ ํ•œ๋‹ค.
  • CORS๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์š”์ฒญ
    • XMLHttpRequest, Fetch API ํ˜ธ์ถœ
    • ์›น ํฐํŠธ
    • WebGL ํ…์Šค์ฒ˜
    • drawImage()๋ฅผ ์‚ฌ์šฉํ•ด ๊ทธ๋ฆฐ ์ด๋ฏธ์ง€/๋น„๋””์˜ค ํ”„๋ ˆ์ž„
    • ์ด๋ฏธ์ง€๋กœ๋ถ€ํ„ฐ ์ถ”์ถœํ•˜๋Š” CSS Shapes
    • ์ด ์ด์™ธ์˜ ์š”์ฒญ์€ ํ—ˆ๋ฝํ•˜์ง€ ์•Š๋Š”๋‹ค.

CORS ์š”์ฒญ ์ข…๋ฅ˜

๊ฐ„๋‹จํ•œ ์š”์ฒญ(Simple Request)

simple-request

  • ๊ธฐ์กด ๋ฐ์ดํ„ฐ์— ๋ถ€์ž‘์šฉ์„ ์ผ์œผํ‚ค์ง€ ์•Š๋Š” ์š”์ฒญ
  • CORS ์‚ฌ์ „ ์š”์ฒญ์„ ๋ฐœ์ƒ์‹œํ‚ค์ง€ ์•Š๋Š” ์š”์ฒญ
  • ์กฐ๊ฑด
    • GET, HEAD, POST ์ค‘ ํ•œ ๊ฐ€์ง€ ๋ฐฉ์‹์„ ์‚ฌ์šฉํ•œ๋‹ค.
    • Custom Header ๋ถˆํฌํ•จ
      • ์˜ˆ์™ธ) Fetch ๋ช…์„ธ์—์„œ CORS-safelisted request-header๋กœ ์ •์˜ํ•œ ํ—ค๋”์˜ ๊ฒฝ์šฐ ์ˆ˜๋™์œผ๋กœ ์„ค์ •ํ•  ์ˆ˜ ์žˆ๋‹ค.
    • POST์ผ ๊ฒฝ์šฐ Content-Type์ด ์•„๋ž˜ ์…‹ ์ค‘ ํ•˜๋‚˜๋ฅผ ๋งŒ์กฑ
      • application/x-www-form-urlencoded
      • multipart/form-data
      • text/plain
  • ๋ชจ๋“  ๋„๋ฉ”์ธ ์š”์ฒญ ๋ฆฌ์†Œ์Šค ์ ‘๊ทผ ํ—ˆ์šฉ: Access-Control-Allow-Origin: *
  • ํŠน์ • ๋„๋ฉ”์ธ์˜ ์š”์ฒญ๋งŒ ๋ฆฌ์†Œ์Šค ์ ‘๊ทผ ํ—ˆ์šฉ: Access-Control-Allow-Origin: ํ—ˆ๋ฝ๋œ๋„๋ฉ”์ธ๋ช…

์‚ฌ์ „ ์š”์ฒญ(Preflighted Request)

preflighted-request

  • ๋ณธ ์š”์ฒญ์„ ๋ณด๋‚ด๊ธฐ ์ „์— ์‚ฌ์ „ ์š”์ฒญ์„ ๋ณด๋‚ด์„œ ์„œ๋ฒ„๊ฐ€ ์ด์— ์‘๋‹ต์ด ๊ฐ€๋Šฅํ•œ ์ง€ ํ™•์ธํ•˜๋Š” ๋ฐฉ๋ฒ•์ด๋‹ค. Cross-site์š”์ฒญ์€ ์œ ์ € ๋ฐ์ดํ„ฐ์— ์˜ํ–ฅ์„ ์ค„ ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๋ฏธ๋ฆฌ ์ „์†ก(preflighted)ํ•˜์—ฌ ์‹ค์ œ ์š”์ฒญ์ด ์ „์†กํ•˜๊ธฐ์— ์•ˆ์ „ํ•œ ์ง€ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ์‚ฌ์ „ ์š”์ฒญ์„ ๋ณด๋‚ด๋Š” ๊ฒฝ์šฐ
    • GET, HEAD, POST ์ด์™ธ์˜ ์š”์ฒญ
    • Custom Header ํฌํ•จ
    • POST ์š”์ฒญ์ผ ๊ฒฝ์šฐ Content-Type์ด ์•„๋ž˜ ์…‹์„ ๋งŒ์กฑํ•˜์ง€ ์•Š์„ ๋•Œ
      • application/x-www-form-urlencoded
      • multipart/form-data
      • text/plain
  • ์‚ฌ์ „ ์š”์ฒญ ๊ณผ์ •
      1. ๋™์ผํ•œ URL์— OPTIONS method๋กœ ์š”์ฒญ(Preflight)
      • OPTIONS : ์„œ๋ฒ„์—์„œ ์ถ”๊ฐ€ ์ •๋ณด๋ฅผ ํŒ๋ณ„ํ•˜๋Š” ๋ฐ ์‚ฌ์šฉํ•˜๋Š” HTTP/1.1 ๋ฉ”์„œ๋“œ์ด๋‹ค. safe๋ฉ”์„œ๋“œ์ด๊ธฐ ๋•Œ๋ฌธ์—, ๋ฆฌ์†Œ์Šค๋ฅผ ๋ณ€๊ฒฝํ•˜๋Š” ๋ฐ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋‹ค.

      • OPTIONS ์š”์ฒญ๊ณผ ํ•จ๊ป˜ ๋‘ ๊ฐœ์˜ ๋‹ค๋ฅธ ์š”์ฒญ ํ—ค๋”๊ฐ€ ์ „์†ก๋œ๋‹ค.

        • Access-Control-Request-Method: POST
        • Access-Control-Request-Headers: X-PINGOTHER, Content-Type
      1. ์š”์ฒญ์— ๋Œ€ํ•ด ์„œ๋ฒ„์—์„œ๋Š” ํ—ˆ์šฉ๋˜๋Š” method, ํ—ˆ์šฉํ•˜๋Š” ํ—ค๋”, ์ฟ ํ‚ค ํ—ˆ์šฉ ์—ฌ๋ถ€๋ฅผ ์‘๋‹ต
      1. ์„œ๋ฒ„๊ฐ€ ์š”์ฒญ์„ ํ—ˆ์šฉํ•œ๋‹ค๋ฉด ๋ณธ ์š”์ฒญ์„ ๋ณด๋‚ธ๋‹ค.

preflighted request ์ดํ›„ ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ๋ฅผ ์ง€์›ํ•˜์ง€ ์•Š๋Š” ๋ธŒ๋ผ์šฐ์ € ๋Œ€์ฒ˜๋ฐฉ์•ˆ

  • preflight ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ๋ฅผ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•ด ์„œ๋ฒ„์ธก ๋™์ž‘ ๋ณ€๊ฒฝ
  • preflight๋ฅผ ๋ฐœ์ƒ์‹œํ‚ค์ง€ ์•Š๋Š” simple request๊ฐ€ ๋˜๋„๋ก ์š”์ฒญ ๋ณ€๊ฒฝ

์ธ์ฆ์ •๋ณด๋ฅผ ํฌํ•จํ•œ ์š”์ฒญ(Request with Credentials)

request-with-credentials

  • ๊ธฐ๋ณธ์ ์œผ๋กœ cross-sitew XMLHttpRequest๋‚˜ Fetchํ˜ธ์ถœ์—์„œ ๋ธŒ๋ผ์šฐ์ €๋Š” ์ž๊ฒฉ ์ฆ๋ฉฐ์—์„ ๋ณด๋‚ด์ง€ ์•Š๋Š”๋‹ค. XMLHttpRequest ๊ฐ์ฒด๋‚˜ Request ์ƒ์„ฑ์ž๊ฐ€ ํ˜ธ์ถœ๋  ๋•Œ ํŠน์ •ํ•œ ํ”Œ๋ž˜๊ทธ๋ฅผ ์„ค์ •ํ•ด์•ผ ํ•œ๋‹ค.
1
2
3
4
5
6
7
8
9
10
11
12
const invocation = new XMLHttpRequest();
const url = 'http://bar.other/resources/credentialed-content/';
    
function callOtherDomain() {
  if (invocation) {
    invocation.open('GET', url, true);
    // ํ”Œ๋ž˜๊ทธ
    invocation.withCredentials = true;
    invocation.onreadystatechange = handler;
    invocation.send(); 
  }
}
  • ์š”์ฒญ์— ์ฟ ํ‚ค๋ฅผ ํฌํ•จํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด XMLHttpRequest ๊ฐ์ฒด์˜ withCredential ํ”„๋กœํผํ‹ฐ ๊ฐ’์„ true๋กœ ์„ค์ •ํ•œ๋‹ค.

  • ํ—ˆ์šฉํ•œ๋‹ค๋ฉด ์„œ๋ฒ„ ์ธก์€ Access-Control-Allow-Credentials ์‘๋‹ต ํ—ค๋”๋ฅผ true๋กœ ์„ค์ •ํ•œ๋‹ค.

  • credentials request์— ์‘๋‹ตํ•  ๋•Œ๋Š” Access-Control-Allow-Originํ—ค๋”์— ๋ฐ˜๋“œ์‹œ ๊ฐ’์„ ์ง€์ •ํ•ด์•ผ ํ•œ๋‹ค. ์™€์ผ๋“œ ์นด๋“œ(*)๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์•ˆ ๋œ๋‹ค. ์™€์ผ๋“œ ์นด๋“œ์ผ ๊ฒฝ์šฐ ์š”์ฒญ์ด ์‹คํ•ดํ•œ๋‹ค.

HTTP ์‘๋‹ต ํ—ค๋”

  • Access-Control-Allow-Origin: ๋‹จ์ผ ์ถœ์ฒ˜๋ฅผ ์ง€์ •ํ•˜์—ฌ ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ํ•ด๋‹น ์ถœ์ฒ˜๊ฐ€ ๋ฆฌ์†Œ์Šค์— ์ ‘๊ทผํ•˜๋„๋ก ํ—ˆ์šฉ๋œ๋‹ค. credentials request ์—†๋Š” ๊ฒฝ์šฐ ์™€์ผ๋“œ ์นด๋“œ๋ฅผ ์‚ฌ์šฉํ•ด ๋ธŒ๋ผ์šฐ์ €์˜ origin์— ์ƒ๊ด€ ์—†์ด ๋ชจ๋“  ๋ฆฌ์†Œ์Šค์— ์ ‘๊ทผํ•˜๋„๋ก ํ—ˆ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.
  • Access-Control-Expose-Headers: ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋Š” ํ—ค๋”๋ฅผ ์„œ๋ฒ„์˜ ํ™”์ดํŠธ๋ฆฌ์ŠคํŠธ์—˜ ์ถ”๊ฐ€
  • Access-Control-Max-Age: preflight request ์š”์ฒญ ๊ฒฐ๊ณผ๋ฅผ ์บ์‹œํ•  ์ˆ˜ ์žˆ๋Š” ์‹œ๊ฐ„ ์ง€์ •
  • Access-Control-Allow-Credentials: credentials request ํ—ˆ์šฉ ์—ฌ๋ถ€ ์ง€์ •
  • Access-Control-Allow-Methods: ๋ฆฌ์†Œ์Šค์— ์ ‘๊ทผํ•  ๋•Œ ํ—ˆ์šฉ๋˜๋Š” ๋ฉ”์„œ๋“œ ์ง€์ •
  • Access-Control-Allow-Headers: preflight request ์š”์ฒญ ์‹œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” HTTP ํ—ค๋” ์ง€์ •

HTTP ์š”์ฒญ ํ—ค๋”

  • Origin: cross-site ์ ‘๊ทผ ์š”์ฒญ ๋˜๋Š” preflight request์˜ ์ถœ์ฒ˜๋กœ, ์ ‘๊ทผ ์ œ์–ด ์š”์ฒญ ์‹œ ํ•ญ์ƒ ์ „์†ก๋œ๋‹ค.
    • Origin: ์š”์ฒญ์‹œ์ž‘๋œ์„œ๋ฒ„URL
  • Access-Control-Request-Method: ์‹ค์ œ ์š”์ฒญ์—์„œ ์–ด๋–ค HTTP ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•  ์ง€ ์„œ๋ฒ„์—๊ฒŒ ์•Œ๋ ค์ฃผ๊ธฐ ์œ„ํ•ด preflight requestํ•  ๋•Œ ์‚ฌ์šฉ๋œ๋‹ค.
  • Access-Control-Request-Headers: ์‹ค์ œ ์š”์ฒญ์—์„œ ์–ด๋–ค HTTP ํ—ค๋”๋ฅผ ์‚ฌ์šฉํ•  ์ง€ ์„œ๋ฒ„์—๊ฒŒ ์•Œ๋ ค์ฃผ๊ธฐ ์œ„ํ•ด preflight requestํ•  ๋•Œ ์‚ฌ์šฉ๋œ๋‹ค.

cross-site XMLHttpRequest ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฐœ๋ฐœ์ž๋Š” ํ”„๋กœ๊ทธ๋ž˜๋ฐ ๋ฐฉ์‹์œผ๋กœ cross-origin ๊ณต์œ  ์š”์ฒญ ํ—ค๋”๋ฅผ ์„ค์ •ํ•  ํ•„์š” ์—†๋‹ค.

์ฐธ์กฐ

  1. https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
  2. https://youtu.be/yTzAjidyyqs
  3. https://en.wikipedia.org/wiki/Cross-origin_resource_sharing
This post is licensed under CC BY 4.0 by the author.

[JS] ์ƒํ™œ์ฝ”๋”ฉ #27: Object

:book: ๋ฆฌํŒฉํ† ๋ง #2 (2): ๋ฆฌํŒฉํ† ๋ง ๊ฐœ๋ก 

Loading comments from Disqus ...