단순 HTTP 요청과 XHR 요청의 차이점을 알아보세요.
오리진 웹 앱 - https://github.com
Origin Web App을 입력하여 웹사이트를 열면 브라우저가 단순 HTTP 요청을 수행합니다. CORS는 여기에 적용되지 않습니다. 우리는 Origin Web App의 콘텐츠 제공을 자발적으로 요청하고 있습니다. 귀하의 브라우저는 아무런 문제 없이 이를 제공할 것입니다. 그러나 XHR 요청의 경우 브라우저의 Origin Web App에 의해 로드된 JavaScript는 리소스를 요청하는 모든 서버(https://netflix.com)에 요청할 수 있습니다. 이제 Origin Web App은 Github가 소유하지만 https://netflix.com은 Netflix가 소유하므로 해당 서버는 잠재적으로 무엇이든 제공할 수 있습니다. Github은 Netflix가 소유한 서버를 제어할 수 없습니다. 이는 잠재적으로 한 웹사이트(금융 웹사이트일 수 있음)에서 원격 서버로 콘텐츠를 훔치는 데 많은 보안 영향을 미칩니다.
다행히 CORS는 주어진 규칙 세트를 사용하여 이 문제를 매우 잘 해결합니다.
클라이언트(브라우저)와 서버 간의 원본 간 요청을 활성화하는 동시에 보안을 유지하면서 리소스를 공유할 수 있도록 W3C에서 정의한 표준입니다. 모든 브라우저는 이러한 표준을 준수하여 타사 서버에서 리소스를 로드하는 것을 방지합니다.
헤더는 요청이 시작된 위치를 나타냅니다. CORS 요청 및 POST 요청과 함께 전송됩니다.
통사론:
Origin: <scheme> "://" <hostname> [":" <port> ]
예:
Origin: https://netflix.com
Origin: http://netflix.com:443
Origin: http://localhost:1443
이 헤더는 실제 요청이 이루어질 때 어떤 요청 방법이 사용될 것인지를 나타내기 위해 사전 요청을 발행할 때 브라우저에서 사용됩니다.
통사론:
Access-Control-Request-Method: <method>
<method>
GET
, POST
또는 DELETE
일 수 있습니다.
예:
Access-Control-Request-Method: POST
이 헤더는 브라우저의 사전 요청에서 다시 사용되어 실제 요청이 이루어질 때 어떤 요청 헤더가 사용될 것인지를 나타냅니다. 여러 개의 헤더를 사용하려면 쉼표로 구분해야 합니다.
통사론:
Access-Control-Request-Headers: <header-name>
Access-Control-Request-Headers: <header-name>, <header-name>
예:
Access-Control-Request-Headers: X-PINGOTHER, Content-Type
중요: 모든 헤더는 CORS 허용 목록에 있거나 X-Custom-Header
와 같은 사용자 정의 헤더여야 합니다.
Accept
Accept-Language
Content-Language
Content-Type
허용되는 값은 application/x-www-form-urlencoded
, multipart/form-data
및 text/plain
입니다.
비행 전 요청에는 다음 헤더가 반환됩니다.
이 헤더는 요청한 Origin이 허용되는지 여부를 나타냅니다. 귀하의 브라우저는 요청된 원본을 이것과 일치시켜 요청의 성공/실패를 선택합니다.
통사론:
Access-Control-Allow-Origin: *
자격 증명이 없는 요청의 경우 *
값을 와일드카드로 지정할 수 있습니다. 이는 브라우저가 모든 Origin의 요청을 허용하도록 지시합니다.
Access-Control-Allow-Origin: <origin>
응답 헤더에 하나의 원본만 수신하면 요청한 Origin
기반으로 하는 서버/웹 앱이 허용되는 경우 동일한 Origin
으로 응답한다는 의미입니다. 또한 서버는 요청 헤더에 따라 달라짐을 나타내기 위해 Vary로 응답해야 합니다.
예:
Access-Control-Allow-Origin: https://github.com
이 헤더의 값 중 하나가 일치하면 브라우저가 실제 요청을 합니다. 와일드카드 *
가 반환되면 모든 메서드가 허용된다는 의미입니다.
통사론:
Access-Control-Allow-Methods: <method>, <method>, ...
Access-Control-Allow-Methods: *
예:
Access-Control-Allow-Methods: GET, POST
요청된 헤더가 모두 허용되면 브라우저는 실제 요청을 수행합니다.
통사론:
Access-Control-Allow-Headers: <header-name>, <header-name>
Access-Control-Allow-Headers: *
예:
Access-Control-Allow-Headers: Accept, Content-Type
와일드카드 *
는 브라우저에 모든 헤더가 허용됨을 알려줍니다.
사전 비행 결과를 캐시할 수 있는 기간을 알려줍니다.
Access-Control-Max-Age: <delta-seconds>
최대 수 초 단위로 결과를 캐시할 수 있습니다.
각 브라우저에는 최대 한도가 있습니다.
일반적으로 캐싱 목적으로 사용되는 헤더를 변경하여 캐시된 응답을 사용할 수 있는지 또는 헤더 값에 따라 다시 캐시해야 하는지 여부를 결정합니다.
CORS에서 서버가 요청된 Origin
기반으로 여러 원본을 허용한다고 가정하면 Access-Control-Allow-Origin
에 특정 URL이 반환됩니다.
통사론:
Vary: <header-name>
예:
github.com이 https://github.com과 https://netflix.com 모두 리소스 요청을 허용한다고 가정해 보겠습니다. 다음 시나리오를 고려하십시오.
시나리오 1:
컬 -X 옵션 https://github.com/api/v1/gists/1
Origin: https://github.com
Access-Control-Request-Method: GET
Access-Control-Request-Headers: Content-Type
Access-Control-Allow-Origin: https://github.com
Vary: Origin
Access-Control-Allow-Methods: GET, POST
Access-Control-Allow-Headers: Content-Type
Access-Control-Max-Age: 600
이제 이 시나리오에서 브라우저는 이 실행 전 요청 결과를 10분 동안 캐시합니다.
시나리오 2:
컬 -X 옵션 https://github.com/api/v1/gists/1
Origin: https://netflix.com
Access-Control-Request-Method: GET
Access-Control-Request-Headers: Content-Type
Access-Control-Allow-Origin: https://netflix.com
Vary: Origin
Access-Control-Allow-Methods: GET, POST
Access-Control-Allow-Headers: Content-Type
Access-Control-Max-Age: 300
이제 Access-Control-Allow-Origin
에 https://netflix.com이 포함되어 있음을 알 수 있습니다. 이는 주어진 Origin
에 따라 응답이 어떻게 달라지는지 보여주는 예입니다. 첫 번째 시나리오와 달리 5분 동안만 캐시되는 이 응답의 max-age도 마찬가지입니다.
일부 요청은 CORS에 대한 사전 요청을 트리거하지 않습니다. 이것을 우리는 단순 요청 이라고 부릅니다. 다음 조건을 충족해야 합니다.
허용되는 방법 중 하나:
사용자 에이전트(예: Connection
또는 User-Agent
)에 의해 자동으로 생성되는 헤더 외에 수동으로 설정할 수 있는 헤더는 CORS 허용 목록에 있는 요청 헤더와 다음뿐입니다.
DPR
Downlink
Save-Data
Viewport-Width
Width
요청에 사용된 XMLHttpRequestUpload
객체에는 이벤트 리스너가 등록되지 않습니다.
요청에는 ReadableStream
개체가 사용되지 않습니다.
예:
요구:
GET /api/v1/public-data/ HTTP/1.1
Host: bar.com
User-Agent: curl/7.69.1
Accept: */*
Origin: https://foo.com
응답 헤더:
HTTP/1.1 200 OK
Access-Control-Allow-Origin: *
Connection: Keep-Alive
Transfer-Encoding: chunked
Content-Type: application/xml
요청이 Access-Control-Allow-Origin
헤더에 *
반환하는 경우. 이는 어느 Host
에서나 요청이 이루어질 수 있음을 의미합니다. 이 경우 브라우저는 비행 전 요청을 하지 않습니다.
브라우저는 실제 요청을 보내도 안전한지 확인하기 위해 비행 전 요청을 합니다.
예:
const xhr = new XMLHttpRequest ( ) ;
xhr . open ( 'POST' , 'https://bar.com/api/v1/post-here/' ) ;
xhr . setRequestHeader ( 'X-PINGOTHER' , 'pingpong' ) ;
xhr . setRequestHeader ( 'Content-Type' , 'application/json;charset=UTF-8' ) ;
xhr . onreadystatechange = handler ;
xhr . send ( JSON . stringify ( { "email" : "[email protected]" } ) ) ;
비행 전 요청:
OPTIONS /api/v1/post-here/
Host: bar.com
User-Agent: curl/7.69.1
Accept: */*
Origin: https://foo.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: X-PINGOTHER, Content-Type
HTTP/1.1 200 OK
Connection: Keep-Alive
Content-Type: application/json
Access-Control-Allow-Origin: https://foo.com
Access-Control-Allow-Methods: GET, POST
Access-Control-Allow-Headers: X-PINGOTHER, Content-Type, Accept
실제 요청:
POST -d '{foo: bar}' /api/v1/post-here/
Host: bar.com
User-Agent: curl/7.69.1
Accept: */*
Origin: https://foo.com
X-PINGOTHER: pingpong
Content-Type: application/json;charset=UTF-8
HTTP/1.1 200 OK
Connection: Keep-Alive
Content-Type: application/json
Access-Control-Allow-Origin: https://foo.com
X-PINGOTHER
와 같은 비표준 헤더가 설정되면 브라우저는 요청을 해도 안전한지 알 수 없습니다. 안전한지 확인하기 위해 브라우저는 X-PINGOTHER
및 Content-Type
포함된 Access-Control-Request-Headers
사용하여 OPTIONS 요청을 합니다. 비행 전 요청의 응답 헤더로 유효성을 검사하면 브라우저가 실제 요청을 합니다.
일반적으로 XHR 요청을 하면 쿠키가 요청과 함께 전달되지 않습니다. 쿠키를 전달해야 하는 경우 XMLHttpRequest
객체에 플래그를 설정해야 합니다.
const xhr = new XMLHttpRequest ( ) ;
const url = 'http://bar.com/api/v1/credentialed-content/' ;
function callOtherDomain ( ) {
if ( invocation ) {
xhr . open ( 'GET' , url , true ) ;
xhr . withCredentials = true ;
xhr . onreadystatechange = handler ;
xhr . send ( ) ;
}
}
요구:
POST -d '{foo: bar}' /api/v1/post-here/
Host: bar.com
User-Agent: curl/7.69.1
Accept: */*
Origin: https://foo.com
X-PINGOTHER: pingpong
Content-Type: application/json;charset=UTF-8
Cookie: _session=NyV14oKXiS6HHopaf9nT
XMLHttpRequest
요청이 이루어지고 withCredentials
플래그가 설정되면 브라우저는 요청 헤더에 Cookie
를 전달합니다.
응답:
HTTP/1.1 200 OK
Connection: Keep-Alive
Content-Type: application/json
Access-Control-Allow-Origin: https://foo.com
Access-Control-Allow-Credentials: true
Cache-Control: no-cache
Pragma: no-cache
Set-Cookie: _session=AjBSqxj4T7bSySNTWeEm; expires=Wed, 31-05-2020 00:00:00 GMT
브라우저에서 Access-Control-Allow-Credentials
true로 설정된 것을 확인하는 경우. Set-Cookie
헤더를 존중하고 쿠키를 설정합니다.
중요: 자격 증명 요청 및 와일드카드 섹션에 언급된 것처럼 Access-Control-Allow-Origin
에 "*" 와일드카드를 설정하면 안 됩니다.
withCredentials
플래그를 설정하여 자격 증명을 요청하는 경우 액세스할 수 있는 헤더를 브라우저에 알릴 수 있도록 서버에서 Access-Control-Expose-Headers
설정해야 합니다.
Cors 이전 세계에서는 기본적으로 CORS 요청의 브라우저에서 응답 헤더에 액세스할 수 없습니다. 따라서 브라우저가 노출된 헤더를 읽기 위해 이 헤더를 찾도록 명시적으로 만들어졌습니다. 이런 방식으로 CORS 사양은 오래된 브라우저가 중단되지 않도록 합니다.
이는 비행 전 요청에 반환됩니다. 브라우저가 이를 확인하면 Set-Cookie
헤더에 액세스할 수 있습니다. 위에서 언급했듯이 일반적인 XHR 요청에서 브라우저는 요청 헤더에 Cookie
전달하지 않고 Set-Cookie
응답 헤더를 읽지 않습니다.
통사론:
Access-Control-Allow-Credentials: true
자격 증명을 사용한 요청 섹션에서 예를 찾을 수 있습니다.
자격 증명 요청이란 쿠키가 XHR 요청에 전달되거나 응답 헤더 Set-Cookie
통해 설정됨을 의미합니다. withCredentials 플래그를 사용하여 XHR 요청이 이루어지면 설정할 쿠키와 함께 응답을 받기를 원합니다. 그러나 Access-Control-Allow-Origin
이 "*"라고 기대할 수는 없습니다. 이는 모든 웹사이트에서 이러한 쿠키를 사용할 수 있다는 의미이기 때문입니다. 이러한 이유로 Access-Control-Allow-Origin
응답 헤더에 "*"가 표시되면 브라우저는 요청을 실패하게 됩니다.
비행 전 요청이 301/302로 응답하는 경우 일부 브라우저는 현재 이를 지원하지 않을 수 있습니다. 다음과 같은 오류가 발생할 수 있습니다.
The request was redirected to 'https://example.com/foo', which is disallowed for cross-origin requests that require preflight
Request requires preflight, which is disallowed to follow cross-origin redirect
참고: 해결 방법은 Mozilla의 사전 요청 및 리디렉션 문서를 확인하세요.
브라우저에는 사용자가 활성화한 경우 모든 third-party
쿠키를 거부하는 설정이 있습니다. 예를 들어, 요청이 https://foo.com
에서 이루어지고 서버가 https://bar.com
에 있는 경우 브라우저는 https://bar.com
에서 보낸 쿠키를 설정하지 않습니다.