[web] CORS (Cross Origin Resource Sharing)
๐ค ๋ชฉ์ฐจ
|
CORS ๋
Cross-Origin Resource Sharing
์น ๋ธ๋ผ์ฐ์ ์์ ์ธ๋ถ ๋๋ฉ์ธ ์๋ฒ์ ํต์ ํ๊ธฐ ์ํ ๋ฐฉ์์ ํ์คํํ specification
- ์๋ฒ์ ํด๋ผ์ด์ธํธ๊ฐ ์ ํด์ง ํค๋๋ฅผ ํตํด ์๋ก ์์ฒญ์ด๋ ์๋ต์ ๋ฐ์ํ ์ง ๊ฒฐ์ ํ๋ ๋ฐฉ์
- ex) header๊ฐ “” ์ผ๋, ์๋ฒ๋ ์์ฒญ์ ๋ฐ์ ~, ํด๋ผ์ด์ธํธ๋ ์๋ต์ ๋ฐ์ ~
<aside> โ What is Same Origin Policy
</aside>
Same Origin Policy
์น ๋ณด์ ์ ์ฑ ์ค ํ๋๋ก, ํ ์ถ์ฒ(origin) ์์ ๋ก๋๋ ๋ฌธ์๋ ์คํฌ๋ฆฝํธ๊ฐ ๋ค๋ฅธ ์ถ์ฒ ์์๊ณผ ์ํธ์์ฉํ์ง ๋ชปํ๋๋ก ์ ์ฝํ๋ค.
→ ๊ฐ์ ๋ ์ ์ฑ : CORS
Same Origin ์ด๋
๋ ํ์ด์ง์ Protocol, Host, Port ๊ฐ ๊ฐ์ ๊ฒฝ์ฐ
ex) js์์ ajax๋ก resource load ํ ๋
origin : http://aaa.yejin.com/dir/index.html
URL ๊ฒฐ๊ณผ ์ด์
CORS ์์ฒญ(Request) ์ ์ข ๋ฅ
Simple Request
์กฐ๊ฑด
- GET,HEAD,POST ์ค ํ๊ฐ์ง ๋ฐฉ์์ ์ฌ์ฉํด์ผ ํ๋ค.
- POST ๋ฐฉ์์ผ ๊ฒฝ์ฐ Content-type์ด ์๋ ์
์ค์ ํ๋์ฌ์ผ ํ๋ค.
- application/x-www-form-urlencoded
- multipart/form-data
- text/plain (default type)
- Custom header๋ฅผ ์ ์กํ์ง ๋ง์์ผ ํ๋ค.
์์
๋ธ๋ผ์ฐ์ url ํธ์ถ : http://domainA
domainA → ajax์์ domainB ์์ resource๋ฅผ ๊ฐ์ ธ์ด
์ฆ, domainA (origin : domainA) ์์ resourceB (host : domainB) ๋ฅผ ํธ์ถ
Request#1 // ์์ฒญ 1
GET path HTTP/1.1
Host : <http://domainB>
Origin : <http://domainA>
Response#1 // ์๋ฒ์ ํ์ 1 origin : domainA
HTTP/1.1 200 OK
Access-Control-Allow-Origin : * // ์๋ฒ์์ ์ด ํค๋์ ๊ฐ์ ๋ณด๊ณ ๊ฒฐ์
Preflight Request
Simple Request ์กฐ๊ฑด์ ํด๋นํ์ง ์์ ์, ๋ธ๋ผ์ฐ์ ๋ Preflight Request ๋ฐฉ์์ผ๋ก ์์ฒญ
์กฐ๊ฑด
- GET,HEAD,POST ์ธ์ ๋ค๋ฅธ ๋ฐฉ์์ผ๋ก ์์ฒญ ๊ฐ๋ฅ
- ๋ค๋ฅธ Content-type์ผ๋ก (application/xml ๋ฑ) ์์ฒญ ๊ฐ๋ฅ
- Custom header ์ฌ์ฉ ๊ฐ๋ฅ
์์ฒญ ๋ฐฉ๋ฒ
- Preflight (์๋น ์์ฒญ)์์ฒญ์ ํ ์ ์๋ ๊ถํ์ด ์๋์ง ํ์ธ
- Request#1 // preflight OPTIONS path HTTP/1.1 Host : <http://domainB> Origin : <http://domainA> Response#1 // prefilght ์๋ต HTTP/1.1 200 OK Access-Control-Allow-Origin : <http://domainA> // domainA๊ฐ ์ถ์ฒ(domainB) ์ฐ๋ ๊ฒ allow Access-Contorl-Allow-Methods : POST,GET,OPTIONS
- ์ค์ ๋ก ์์ฒญํ๋ ค๋ ๊ฒฝ๋ก์ ๊ฐ์ URL์ ๋ํด OPTION ๋ฉ์๋๋ก ์์ฒญ์ ๋ฏธ๋ฆฌ ๋ ๋ ค ๋ณธ ํ,
- ์ค์ ์์ฒญ
- Request#2 // ์ค์ ์์ฒญ POST path HTTP/1.1 Host : <http://domainB> Origin : <http://domainA> Respons#2 HTTP/1.1 200 OK Access-Control-Allow-Origin : <http://domainA>
Request with Credential
HTTP Cookie์ HTTP Authentication ์ ๋ณด๋ฅผ ์ธ์ํ ์ ์๊ฒ ํด์ฃผ๋ ์์ฒญ
protocol, port ๊ฐ ๋ค๋ฅด๊ฒ ๋ ๊ฒฝ์ฐ, session์ด ์ ์ค ๋์ง ์๊ฒ ํ๋ ์กฐ์น
ํด๋ผ์ด์ธํธ ๋จ
xhr.withCredential = true
์๋ฒ ๋จ
Access-Control-Allow-Credentials:true
Access-Control-Allow-Origin : <http://domainA> // * ์๋จ
// Request ajax ๋ถ๋ถ
$.ajax({
type : ,
contentType: ,
url: ,
success:function(){
},
error:function(){
},
xhrFields:{
withCredentials:true // credential ์์ฒญ์ ์ฃผ์ด์ผ ํจ
},
});
Request without Credential
default๋ก Non-Credential ์์ฒญ
( xhr.withCredentials=true ์์๋ง credential ์์ฒญ)
Server์์ CORS ์์ฒญ ํธ๋ค๋งํ๋ ๋ฐฉ๋ฒ
EX1) Preflight Request
//OPTION ๋ฉ์๋์ ์์ฒญ์ ๋ฐ์์ ์ปจํธ๋กค
//header ์์
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET,POST,DELETE,OPTIONS
Access-Control-Max-Age:3600
Access-Control-Allow-Header: Origin,Accept,X-Requested-With,Content-Type,Access-Control-Request-Method,Access-Control-Request-Headers,Authorization (*๋ก ํ์ฉ์๋จ)
Ex2) Request with Credential
// With Credential
Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin : ๋๋ฉ์ธ๋ช
// * ์๋จ
ํ ์คํธ
โ ๏ธ CORS error
host ๊ฐ ๋ค๋ฅธ ์์ฒญ
$('#cors1__err').click(function() {
console.log('host diff');
$.ajax({
url : '<http://bbb.yejin.co.kr/DynamicWeb03/index.jsp>',
success : function() {
alert('success');
},
error : function() {
alert('error');
}
})
});
Access to XMLHttpRequest
at '<http://bbb.yejin.co.kr/DynamicWeb03/index.jsp>'
from origin '<http://aaa.yejin.co.kr>'
has been blocked by CORS policy:
No 'Access-Control-Allow-Origin' header is present on the requested resource.
// Request Headers
GET /DynamicWeb03/index.jsp HTTP/1.1
Accept: */*
Accept-Encoding: gzip, deflate
Accept-Language: ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7
Connection: keep-alive
Host: bbb.yejin.co.kr
Origin: <http://aaa.yejin.co.kr>
Referer: <http://aaa.yejin.co.kr/>
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64)
AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36
// Response Headers
HTTP/1.1 200
Set-Cookie: JSESSIONID=0C4002012D3D81EA3F5044992558C040; Path=/DynamicWeb03; HttpOnly
Content-Type: text/html;charset=EUC-KR
Content-Length: 239
Date: Wed, 13 Jul 2022 05:12:54 GMT
Protocol http → https
$('#cors2__err').click(function() {
console.log('protocol diff');
$.ajax({
url : '<https://aaa.yejin.co.kr/DynamicWeb01/index.jsp>',
success : function() {
alert('success');
},
error : function() {
alert('error');
}
})
});
Access to XMLHttpRequest
at '<https://aaa.yejin.co.kr/DynamicWeb01/index.jsp>'
from origin '<http://aaa.yejin.co.kr>'
has been blocked by CORS policy:
No 'Access-Control-Allow-Origin' header is present on the requested resource.
Protocol https → http : Mixed Content (It is not CORS)
Mixed Content:
The page at '<https://aaa.yejin.co.kr/DynamicWeb01/index.jsp>'
was loaded over HTTPS,
but requested an insecure XMLHttpRequest endpoint
'<http://bbb.yejin.co.kr/DynamicWeb03/index.jsp>'.
This request has been blocked; the content must be served over HTTPS.
Port ๊ฐ ๋ค๋ฅธ ์์ฒญ
$('#cors3__err').click(function() {
console.log('port diff');
$.ajax({
url : '<http://aaa.yejin.co.kr:8080/DynamicWeb01/index.jsp>',
success : function() {
alert('success');
},
error : function() {
alert('error');
}
})
});
Access to XMLHttpRequest
at '<http://aaa.yejin.co.kr:8080/DynamicWeb01/index.jsp>'
from origin '<http://aaa.yejin.co.kr>'
has been blocked by CORS policy:
No 'Access-Control-Allow-Origin' header is present on the requested resource.
๐ Acess-Control-Allow-Origin
// Web ์๋ฒ๋ฅผ ํตํด ์๋ฒ์ Response Header์ ์๋ ๋ด์ฉ ์ถ๊ฐ
cors_all Action = AddResponse,
FieldName = "Access-Control-Allow-Origin",
FieldValue = "*"
// Request Header
GET /DynamicWeb02/index.jsp HTTP/1.1
...
Host: bbb.yejin.co.kr
Origin: <http://aaa.yejin.co.kr>
Referer: <http://aaa.yejin.co.kr/>
...
// Response Header
HTTP/1.1 200
Access-Control-Allow-Origin: *
Set-Cookie: JSESSIONID=28C41758BF3E387AB88E616A3F55631A; Path=/DynamicWeb02; HttpOnly
Content-Type: text/html;charset=EUC-KR
Content-Length: 424
Date: Wed, 13 Jul 2022 05:51:52 GMT
with Credential → ์ธ์ ์ ์ง ํ ์คํธ
// origin
JSESSIONID=EE1C98820B6693EF2E710B74697B8074
// no credential -> session ์ฌ๋ฐ๊ธ
HTTP/1.1 200
Access-Control-Allow-Origin: *
Set-Cookie: JSESSIONID=A21D1937053DE17F3C29B91FEF4D0B78; Path=/DynamicWeb01; HttpOnly
Content-Type: text/html;charset=EUC-KR
Content-Length: 424
Date: Wed, 13 Jul 2022 06:12:13 GMT
// credential -> session ์ ์ง
HTTP/1.1 200
Access-Control-Allow-Origin: <http://aaa.yejin.co.kr>
Access-Control-Allow-Credentials: true
Content-Type: text/html;charset=EUC-KR
Content-Length: 424
Date: Wed, 13 Jul 2022 06:11:40 GMT
...
Cookie: JSESSIONID=EE1C98820B6693EF2E710B74697B8074 // request header
์ฐธ๊ณ
naver.com ์ ์ ๋ ฅ ํ ๊ฐ๋ฐ์๋๊ตฌ๋ฅผ ํตํด ํ์ธํ๋ฉด,
์ฌ๋ฌ resource๋ฅผ ๋ฐ๊ธฐ ์ํด origin์ด ๋ค๋ฅธ ์์ฒญ์ response header์๋ access-control-allow-origin ์ต์ ์ด ์๋ ๊ฒ์ ํ์ธ ํ ์ ์๋ค.
์ค์ ๋ฌด์์ ๋ง์ด ๊ฒช์ ์ ๋ฐ์ ์๋ ๋ณด์์ ์ฑ ์ด๋ฏ๋ก, ์น ๊ฐ๋ฐ ๊ฐ๋ฐ์๋ผ๋ฉด CORS ์ ์ฑ ์ ์๊ณ ์์ด์ผ ํ๋ค๊ณ ์๊ฐ๋๋ค.
์ ๋ฆฌ
์ง๋ฌธ : CORS๋ ๋ฌด์์ด๋ฉฐ ์ด๊ฒ์ ๋ํด์ ์ค๋ช ํด๋ณด์ธ์.
CORS๋ ์น๊ฐ๋ฐ์ ํ๋ค๊ฐ ํํ ๋ง๋ ์ ์๋ ์ด์์ ๋๋ค. ๋๊ฐ๋ ํ๋ก ํธ์๋ ๊ฐ๋ฐ์์ ๋ก์ปฌ์์ API ์๋ฒ์ ์์ฒญ์ ๋ณด๋ผ ๋ ํํ๊ฒ ๋ฐ์ํฉ๋๋ค.
์๋ก ๋ค๋ฅธ ๋๋ฉ์ธ๊ฐ์ ์์์ ๊ณต์ ํ๋ ๊ฒ์ ๋ปํฉ๋๋ค. ๋๋ถ๋ถ์ ๋ธ๋ผ์ฐ์ ์์๋ ์ด๋ฅผ ๊ธฐ๋ณธ์ ์ผ๋ก ์ฐจ๋จํ๋ฉฐ, ์๋ฒ์ธก์์ ํค๋๋ฅผ ํตํด์ ์ฌ์ฉ๊ฐ๋ฅํ ์์์ ์๋ ค์ค๋๋ค.
preflight request๋ ์ค์ ์์ฒญ์ ๋ณด๋ด๋ ์์ ํ์ง ํ๋จํ๊ธฐ ์ํด ์ฌ์ ์ ๋ณด๋ด๋ ์์ฒญ์ ๋๋ค. OPTIONS ๋ฉ์๋๋ก ์์ฒญํ๋ฉฐ CORS๋ฅผ ํ์ฉํ๋์ง ํ์ธํฉ๋๋ค. CORS๊ฐ ํ์ฉ๋ ์น์๋ฒ๋ผ๋ฉด ์ฌ์ฉ ๊ฐ๋ฅํ ๋ฆฌ์์ค๋ฅผ ํค๋์ ๋ด์ ์๋ตํฉ๋๋ค.