CORS


Cross-Origin Resource Sharing, CORS ๋ž€ ์„œ๋กœ ๋‹ค๋ฅธ Origin ๊ฐ„์— ์ž์›์„ ๊ณต์œ ํ•˜๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•œ๋‹ค.

CORS ๋ฅผ ์ดํ•ดํ•˜๊ธฐ ์œ„ํ•ด์„  ๋จผ์ € Origin ๊ณผ SOP ๋ผ๋Š” ๊ฐœ๋…์„ ์•Œ์•„์•ผ ํ•œ๋‹ค.

Origin

Origin ์€ ์ง์—ญํ•˜๋ฉด ์ถœ์ฒ˜ ๋กœ URL ์˜ Protocol Host Port ๋กœ ์ •์˜๋œ๋‹ค. ์ฆ‰, Origin ์ด ๊ฐ™๋‹ค๋Š” ๊ฒƒ์€ URL ์˜ Protocol Host Port ๊ฐ€ ๋ชจ๋‘ ๋™์ผํ•˜๋‹ค๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•œ๋‹ค.

์ฒ˜์Œ ๋ธŒ๋ผ์šฐ์ €๋ฅผ ํ‚ค๊ณ  https://www.naver.com ์„ ์ ‘์†ํ•˜๋ฉด index.html ๋ฉ”์ธ ํŽ˜์ด์ง€๋ฅผ ๋ฐ›์„ํ…๋ฐ ํ•ด๋‹น ํŽ˜์ด์ง€์—์„œ ์ „์†กํ•˜๋Š” ๋ชจ๋“  ์š”์ฒญ์˜ Origin ์€ https://www.naver.com ์ด ๋˜๋Š” ๊ฒƒ์ด๋‹ค.

์œ„ ์š”์ฒญ์€ ๋„ค์ด๋ฒ„ ๊ธˆ์œต ํŽ˜์ด์ง€์—์„œ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋Š” ์š”์ฒญ์ด๋‹ค. Origin ์ด ๋‹ค๋ฅธ Origin ์œผ๋กœ ์š”์ฒญ์„ ๋ณด๋‚ผ ๋•Œ Origin ํ—ค๋”๊ฐ€ ํฌํ•จ๋˜์–ด ์žˆ๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

SOP

Same-Origin Policy, SOP ๋Š” ์ง์—ญํ•˜๋ฉด ๋™์ผ ์ถœ์ฒ˜ ์ •์ฑ… ์œผ๋กœ ๋‹ค๋ฅธ Origin ์œผ๋กœ ์š”์ฒญ์„ ๋ณด๋‚ด๋Š” ๊ฒƒ์„ ๊ธˆ์ง€ํ•˜๋Š” ๋ธŒ๋ผ์šฐ์ €์˜ ๊ธฐ๋ณธ ๋ณด์•ˆ ์ •์ฑ…์ด๋‹ค. ์ฆ‰, ๋™์ผํ•œ Origin ์œผ๋กœ๋งŒ ์š”์ฒญ์„ ๋ณด๋‚ผ ์ˆ˜ ์žˆ๊ฒŒ๋” ๊ฐ•์ œํ•˜๋Š” ๊ฒƒ์ด๋‹ค.

๊ณผ๊ฑฐ ์›น์€ ํ”„๋ก ํŠธ์—”๋“œ์™€ ๋ฐฑ์—”๋“œ๋ฅผ ๋ณ„๋„๋กœ ๊ตฌ๋ถ„ํ•˜์ง€ ์•Š๋Š” ๋Œ€์‹  ์„œ๋ฒ„๊ฐ€ ์ง์ ‘ ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•œ ๊ฒฐ๊ณผ๋ฅผ HTML ๋ฌธ์„œ๋กœ ๋งŒ๋“ค์–ด ํด๋ผ์ด์–ธํŠธ์—๊ฒŒ ์ œ๊ณตํ–ˆ๋‹ค. ์ด๋Š” ๋ชจ๋“  ์ฒ˜๋ฆฌ๊ฐ€ ๊ฐ™์€ Origin ์—์„œ ์ผ์–ด๋‚œ๋‹ค๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•œ๋‹ค.

์ฆ‰, ๊ณผ๊ฑฐ ๋ธŒ๋ผ์šฐ์ €์—์„  ๋‹ค๋ฅธ Origin ์œผ๋กœ ์š”์ฒญ์„ ๋ณด๋‚ผ ํ•„์š”๊ฐ€ ์—†์—ˆ๊ธฐ ๋•Œ๋ฌธ์— ๋‹ค๋ฅธ Origin ์œผ๋กœ ์š”์ฒญ์„ ๋ณด๋‚ด๋Š” ๊ฒƒ์„ ์•…์˜์ ์ธ ํ–‰์œ„๋กœ ๊ฐ„์ฃผํ•˜์—ฌ SOP ๋ผ๋Š” ์ •์ฑ…์„ ํ†ตํ•ด ๋ธŒ๋ผ์šฐ์ €์—์„œ ๋ง‰์•˜๋˜ ๊ฒƒ์ด๋‹ค.

์‹œ๋Œ€๊ฐ€ ํ๋ฅด๊ณ  ์›น ๊ธฐ์ˆ ์ด ๋ฐœ์ „ํ•จ์— ๋”ฐ๋ผ ๋‹ค๋ฅธ ์ถœ์ฒ˜๋กœ ์š”์ฒญ์„ ํ•˜๋Š” ์ˆ˜์š”๊ฐ€ ์ฆ๊ฐ€ํ–ˆ๋‹ค. ํ•˜์ง€๋งŒ SOP ๋ผ๋Š” ๋ธŒ๋ผ์šฐ์ € ์ •์ฑ… ๋•Œ๋ฌธ์— ์ผ๋ฐ˜์ ์ธ ๋ฐฉ๋ฒ•์œผ๋ก  ๋ถˆ๊ฐ€๋Šฅํ–ˆ๊ธฐ์— CORS ๋ผ๋Š” ๊ฐœ๋…์ด ๋“ฑ์žฅํ•œ ๊ฒƒ์ด๋‹ค.

์ฆ‰, CORS ๋Š” ๊ธฐ์กด SOP ์— ์˜ํ•ด ์ œํ•œ๋˜์—ˆ๋˜, ๋‹ค๋ฅธ Origin ์œผ๋กœ ์š”์ฒญ์„ ๋ณด๋‚ด๊ธฐ ์œ„ํ•ด ์ง€์ผœ์•ผ ํ•˜๋Š” ์ •์ฑ…์ธ ๊ฒƒ์ด๋‹ค.

SOP ์˜ ํ•„์š”์„ฑ

๋ธŒ๋ผ์šฐ์ €๋Š” ์™œ SOP ๊ฐ™์€ ์ •์ฑ…์„ ๋งŒ๋“ค์–ด์„œ ํ†ต์‹ ํ•˜๊ธฐ ์œ„ํ•ด CORS ๊ฐ™์€ ๋ถ€๊ฐ€์ ์ธ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•ด์•ผ ํ• ๊นŒ? SOP ๊ฐ€ ๋งŒ์•ฝ ์—†์—ˆ๋‹ค๋ฉด ์–ด๋–ค ์ผ์ด ๋ฐœ์ƒํ•˜๊ธฐ์— ์ด๋Ÿฐ ์ •์ฑ…์ด ํ•„์š”ํ• ๊นŒ?

์„ ๋Ÿ‰ํ•œ ์‚ฌ์šฉ์ž๊ฐ€ ๊ตฌ๊ธ€์— ๋„ค์ด๋ฒ„์— ๋กœ๊ทธ์ธ์ด ๋œ ์ƒํƒœ๋กœ www.hacker.com ๋ผ๋Š” ํ”ผ์‹ฑ ์‚ฌ์ดํŠธ์— ์ ‘๊ทผํ–ˆ๋‹ค๊ณ  ์ƒ๊ฐํ•ด๋ณด์ž. ํ”ผ์‹ฑ ์‚ฌ์ดํŠธ์—” ๋„ค์ด๋ฒ„์˜ ๊ฐœ์ธ ์ •๋ณด๋ฅผ ์š”์ฒญํ•˜๋Š” ์Šคํฌ๋ฆฝํŠธ๊ฐ€ ์ž‘์„ฑ๋˜์–ด ์žˆ๋‹ค.

ํ”ผ์‹ฑ ์‚ฌ์ดํŠธ์— ์ ‘์†ํ•˜๋Š” ์ˆœ๊ฐ„ ์Šคํฌ๋ฆฝํŠธ๊ฐ€ ์‹คํ–‰๋˜์–ด ๋ณธ์ธ์˜ ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ๊ฐ€์ง€๊ณ  ์žˆ๋˜ ๋„ค์ด๋ฒ„ ์ธ์ฆ ์ •๋ณด๋ฅผ ์‚ฌ์šฉํ•ด ๊ฐœ์ธ ์ •๋ณด๊ฐ€ ๋…ธ์ถœ๋˜๋Š” ์ผ์ด ๋ฐœ์ƒํ•œ๋‹ค. ์„œ๋ฒ„ ์ž…์žฅ์—์„  ์ •์ƒ์ ์ธ ์ธ์ฆ์ •๋ณด๋ฅผ ํ†ตํ•ด ์š”์ฒญ์„ ๋ณด๋ƒˆ๊ธฐ ๋•Œ๋ฌธ์— ํ•ด๋‹น ์š”์ฒญ์ด ์•…์˜์ ์ธ ์š”์ฒญ์ด๋ผ๋Š” ๊ฒƒ์„ ํŒ๋ณ„ํ•  ์ˆ˜ ์—†์„ ๊ฒƒ์ด๋‹ค. ์ด๊ฒƒ์ด ๋ฐ”๋กœ CSRF ๊ณต๊ฒฉ์ด๋‹ค.

SOP ์ •์ฑ…์€ ์ด๋Ÿฐ ๋ฌธ์ œ๋ฅผ ๊ทผ๋ณธ์ ์œผ๋กœ ํ•ด๊ฒฐํ•œ๋‹ค. www.hacker.com ์ด๋ผ๋Š” Origin ์—์„œ ๋„ค์ด๋ฒ„์—๊ฒŒ ์š”์ฒญ์„ ๋ณด๋ƒˆ์œผ๋‹ˆ ๋ธŒ๋ผ์šฐ์ € ์ž…์žฅ์—์„  ์„œ๋กœ ๋‹ค๋ฅธ Origin ์—์„œ ์š”์ฒญ์„ ํ•˜๋Š” ๊ฒƒ์œผ๋กœ ๊ฐ„์ฃผํ•˜๊ฒŒ ๋˜์–ด ํ•ด๋‹น ์š”์ฒญ ์ž์ฒด๋ฅผ ๋ง‰์•„๋ฒ„๋ฆฌ๋Š” ๊ฒƒ์ด๋‹ค.

CORS ์ ‘๊ทผ ์ œ์–ด ์‹œ๋‚˜๋ฆฌ์˜ค


Simple Request

CORS ๋ฅผ ๋งŒ์กฑํ•˜๊ธฐ ์œ„ํ•ด์„  Preflight ์š”์ฒญ์„ ํ†ตํ•ด CORS ํ—ˆ์šฉ ์—ฌ๋ถ€๋ฅผ ๋จผ์ € ํ™•์ธํ•ด์•ผ ํ•˜๋Š”๋ฐ, ์•„๋ž˜ ์กฐ๊ฑด์„ ๋ชจ๋‘ ์ถฉ์กฑํ•˜๋Š” ์š”์ฒญ์˜ ๊ฒฝ์šฐ Preflight ์š”์ฒญ์ด ๋ถˆํ•„์š”ํ•œ ์•ˆ์ „ํ•œ ์š”์ฒญ์ด๋ผ๊ณ  ํŒ๋‹จํ•˜์—ฌ ํ•œ ๋ฒˆ๋งŒ ์š”์ฒญํ•˜๋Š” Simple Request ๋ฅผ ๋ณด๋‚ธ๋‹ค.

  • GET POST HEAD ๋ฉ”์„œ๋“œ๋งŒ ํ—ˆ์šฉ
  • Accept Accept-Language Content-Language Content-Type ํ—ค๋”๋งŒ ํ—ˆ์šฉ
  • Content-Type ์€ application/x-www-form-urlencoded multipart/form-data text/plain ๋งŒ ํ—ˆ์šฉ
GET /data HTTP/1.1
Origin: https://foo.example

๋ธŒ๋ผ์šฐ์ €๊ฐ€ ๋‹ค๋ฅธ ์ถœ์ฒ˜๋กœ์˜ ์š”์ฒญ์„ ๋ณด๋‚ผ ๋•Œ ์ž๋™์œผ๋กœ Origin ํ—ค๋”๋ฅผ ์ถ”๊ฐ€ํ•˜์—ฌ ๋ณด๋‚ธ๋‹ค.

HTTP/1.1 200 OK
Access-Control-Allow-Origin: *

์„œ๋ฒ„๋Š” Allow-Control-Allow-Origin ํ—ค๋”์— ํ—ˆ์šฉ๋œ Origin ์˜ ๋ชฉ๋ก์„ ๋‹ด์•„์„œ ์‘๋‹ตํ•œ๋‹ค. ์™€์ผ๋“œ์นด๋“œ๋ฅผ ์‚ฌ์šฉํ•ด ๋ชจ๋“  ์ถœ์ฒ˜๋ฅผ ํ—ˆ์šฉํ•  ์ˆ˜๋„ ์žˆ๋‹ค.

๋ธŒ๋ผ์šฐ์ €๋Š” ์š”์ฒญ์˜ Origin ํ—ค๋”์— ๋‹ด๊ธด ์ถœ์ฒ˜ ์ •๋ณด๊ฐ€ ์‘๋‹ต์˜ Access-Control-Allow-Origin ํ—ค๋”์— ๋‹ด๊ฒจ์žˆ์œผ๋ฉด ํ•ด๋‹น ์š”์ฒญ์„ ์•ˆ์ „ํ•˜๋‹ค๊ณ  ๊ฐ„์ฃผํ•˜๊ณ  ์‘๋‹ต์„ ๊ฐ€์ ธ์˜จ๋‹ค.

๋งŒ์•ฝ ๊ทธ๋ ‡์ง€ ์•Š๋‹ค๋ฉด ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ํ•ด๋‹น ์‘๋‹ต์„ ์ž„์˜๋กœ ํŒŒ๊ธฐํ•˜๊ณ  ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๋กœ ์‘๋‹ต์˜ ๋‚ด์šฉ์„ ์ „๋‹ฌํ•˜์ง€ ์•Š๋Š”๋‹ค.

Preflight Request

์‹ค์ œ ์š”์ฒญ์„ ๋ณด๋‚ด๊ธฐ ์ „์— OPTIONS ๋ฉ”์„œ๋“œ๋กœ ์‚ฌ์ „ ์š”์ฒญ์„ ๋ณด๋‚ด์„œ CORS ํ—ˆ์šฉ ์—ฌ๋ถ€๋ฅผ ๋จผ์ € ํ™•์ธํ•˜๋Š” ๋ฐฉ์‹์ด๋‹ค.

OPTIONS /data HTTP/1.1
Origin: https://foo.example
Access-Control-Request-Method: POST
Access-Control-Reqeust-Headers: Content-Type, Custom-Header

Preflight ์š”์ฒญ์€ Simple Request ์™€ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ์š”์ฒญ์— Origin ํ—ค๋”๋ฅผ ์ถ”๊ฐ€ํ•˜๊ณ  ์‹ค์ œ ์š”์ฒญ์˜ ๋ฉ”์„œ๋“œ๋ฅผ Access-Control-Request-Method ํ—ค๋”์— ๋‹ด๊ณ  ์‹ค์ œ ์š”์ฒญ์˜ ์ถ”๊ฐ€ ํ—ค๋” ๋ชฉ๋ก์„ Access-Control-Request-Headers ํ—ค๋”์— ๋‹ด์•„์„œ ๋ณด๋‚ธ๋‹ค.

์‹ค์ œ ์š”์ฒญ ์ด์ „์— ์ด๋Ÿฐ ๋ฉ”์„œ๋“œ์™€ ํ—ค๋”๋กœ ์š”์ฒญ์„ ๋ณด๋‚ผ๊ฑด๋ฐ ์„œ๋ฒ„ CORS ์ •์ฑ…์—์„œ ํ—ˆ์šฉํ•˜๋Š”์ง€ ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์„œ๋ฒ„์—๊ฒŒ ๋ฏธ๋ฆฌ ๋ฌผ์–ด๋ณด๋Š” ์ ˆ์ฐจ์ด๋‹ค.

HTTP/1.1 200 OK
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: Content-Type, Custom-Header
Access-Control-Max-Age: 86400

์‘๋‹ต ์—ญ์‹œ Simple Request ์™€ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ Access-Control-Allow-Origin ํ—ค๋”๊ฐ€ ๋‹ด๊ธฐ๊ณ  ์„œ๋ฒ„์ธก์—์„œ ํ—ˆ์šฉํ•˜๋Š” ๋ฉ”์„œ๋“œ ๋ชฉ๋ก์ด ๋‹ด๊ธด Access-Control-Allow-Methods ํ—ค๋”์™€ ํ—ˆ์šฉํ•˜๋Š” ํ—ค๋” ๋ชฉ๋ก์ด ๋‹ด๊ธด Access-Control-Allow-Headers ํ—ค๋” ๊ทธ๋ฆฌ๊ณ  Preflight ์˜ ์บ์‹œ ๊ธฐ๊ฐ„์ธ Access-Control-Max-Age ๋ฅผ ๋ณด๋‚ด์ค€๋‹ค.

Credentialed Request

์ฟ ํ‚ค ๋˜๋Š” ํ† ํฐ๊ณผ ๊ฐ™์ด ์‚ฌ์šฉ์ž ์‹๋ณ„ ์ •๋ณด๊ฐ€ ๋‹ด๊ธด ์š”์ฒญ์— ๋Œ€ํ•ด์„œ๋Š” ์กฐ๊ธˆ ๋” ์—„๊ฒฉํ•˜๊ฒŒ ์ฒ˜๋ฆฌํ•œ๋‹ค.

GET /data HTTP/1.1
Origin: https://foo.example
Cookie: sessionId=123

ํด๋ผ์ด์–ธํŠธ๋Š” ์š”์ฒญ์„ ๋ณด๋‚ผ ๋•Œ ์ธ์ฆ์ •๋ณด๋ฅผ ํฌํ•จํ•˜๊ธฐ ์œ„ํ•ด Credentials ์˜ต์…˜์„ ๋ณ„๋„๋กœ ์„ค์ •ํ•ด์•ผํ•œ๋‹ค. ์ด๋Ÿฌํ•œ ์„ค์ •์„ ํ•ด์ฃผ์ง€ ์•Š์œผ๋ฉด ์ฟ ํ‚ค ๋“ฑ์˜ ์ธ์ฆ ์ •๋ณด๋Š” ์ ˆ๋Œ€ ์ž๋™์œผ๋กœ ์š”์ฒญ์— ํฌํ•จ๋˜์ง€ ์•Š๋Š”๋‹ค.

HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://foo.example
Allow-Control-Allow-Credentials: true

์„œ๋ฒ„๋Š” ์ด๋Ÿฌํ•œ ์š”์ฒญ์— ๋Œ€ํ•ด์„œ ์ผ๋ฐ˜์ ์ธ CORS ์š”์ฒญ๊ณผ ๋‹ค๋ฅด๊ฒŒ Access-Control-Allow-Origin ํ—ค๋”์— ๋ช…ํ™•ํ•œ Origin ์„ ๋ช…์‹œํ•ด์ฃผ์–ด์•ผ ํ•˜๋ฉฐ Allow-Control-Allow-Credentials ํ—ค๋”๋Š” true ๋กœ ์„ค์ •๋˜์–ด์•ผ ํ•œ๋‹ค.

๊ทธ๋ ‡์ง€ ์•Š์„ ๊ฒฝ์šฐ ๋ธŒ๋ผ์šฐ์ €์— ์˜ํ•ด ์‘๋‹ต์ด ๊ฑฐ๋ถ€๋œ๋‹ค.

์ •๋ฆฌ


CORS ๋Š” ์„œ๋ฒ„๊ฐ€ ์•„๋‹Œ ๋ธŒ๋ผ์šฐ์ € ๊ตฌํ˜„ ์ŠคํŽ™์— ํฌํ•จ๋œ ์ •์ฑ…์ด๋‹ค. ๋•Œ๋ฌธ์— ์„œ๋ฒ„๋Š” CORS ์œ„๋ฐ˜ ์—ฌ๋ถ€์™€ ๊ด€๊ณ„์—†์ด ์š”์ฒญ์ด ๋“ค์–ด์˜ค๋ฉด ์ผ๋‹จ ์ฒ˜๋ฆฌํ•˜๊ณ  ์‘๋‹ตํ•œ๋‹ค. ๊ทธ ์‘๋‹ต์„ ๋ฐ›์•„์„œ ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์‘๋‹ต ํ—ค๋”๋ฅผ ํ™•์ธํ•˜๊ณ  ์‘๋‹ต์˜ ํŒŒ๊ธฐ ์—ฌ๋ถ€๋ฅผ ๊ฒฐ์ •ํ•˜๊ฒŒ ๋œ๋‹ค.

GET HEAD ์™€ ๊ฐ™์€ ๋‹จ์ˆœ ์กฐํšŒ ์š”์ฒญ์€ ์ƒ๊ด€ ์—†์–ด๋„ POST PUT DELETE ์™€ ๊ฐ™์€ ๋ฉ”์„œ๋“œ๋Š” ์„œ๋ฒ„์— ๋ถ€์ž‘์šฉ์„ ์•ผ๊ธฐํ•  ์ˆ˜ ์žˆ๋‹ค. ์ด๋Š” ์‘๋‹ต์ด ์œ ํšจํ•˜์ง€ ์•Š์•„ ํŒŒ๊ธฐํ•œ ๋ธŒ๋ผ์šฐ์ €์˜ ์˜์‚ฌ์™€ ์ƒ๊ด€์—†์ด ๋ฐœ์ƒํ•œ๋‹ค.

Preflight ๋Š” ์‹ค์ œ ์š”์ฒญ์ด CORS ๋ฅผ ์œ„๋ฐ˜ํ•˜์ง€ ์•Š๋Š”์ง€ ๋ฏธ๋ฆฌ ํ™•์ธํ•˜์—ฌ ์„œ๋ฒ„์˜ ๋ถ€์ž‘์šฉ์„ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•ด ์ „์†กํ•œ๋‹ค.

ํ•˜์ง€๋งŒ POST ๊ฐ™์€ ๊ฒฝ์šฐ ์กฐ๊ฑด๋งŒ ๋งŒ์กฑํ•œ๋‹ค๋ฉด Preflight ๋Œ€์‹  Simple Request ๋กœ ์ „์†ก๋  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๋ฐฑ์—”๋“œ์—์„œ ์ด์— ๋Œ€ํ•œ ์ฒ˜๋ฆฌ๊ฐ€ ํ•„์š”ํ•˜๋‹ค.

References