대학교 기말 프로젝트에서 프론트와 협업을 하던 도중 CORS에러를 만나게 되었습니다.
그 때는 아 이런 문제가 있구나 하고 해결책을 찾은 후에 넘어 갔지만, 다시 생각해보니 정리를 한번 해야 할 것 같아서 정리를 하려고합니다.
CORS가 뭐에요?
CORS는 Cross-Origin Resource Sharing의 약자로 직역하면 "교차 출처 리소스 공유" 입니다.
즉, 동일한 출처가 아닌 다른 출처에서 데이터를 주고 받는 것을 허용하는 정책입니다.
제가 멈추었던 부분이 여기까지 였습니다. 왜냐하면 아 이것때문에 문제가 발생했고 이렇게하면 고칠 수 있겠구나 라고요.
하지만 저희는 여기서 더욱 사고 해야합니다.
"왜 이러한 정책이 필요할까?"
이 문제를 이해하려면 "동일 출처 정책" 에 대해서 알아야 합니다.
Same Origin Policy(동일 출처 정책)
동일 출처 정책은 웹 브라우저에서 보안을 강화하기 위해 동일한 출처에서만 리소스를 주고 받도록 하는 정책입니다.
계속 "출처" 라는 단어가 반복되는데, 출처가 도대체 뭘까요?
쉽게 말하면 "URL 주소" 입니다.
하지만 여기서 말하는 "동일한 출처" 즉 "동일한 URL 주소" 는 정확하게 똑같은 URL을 말하는 것이 아닙니다.
동일한 출처는 URL 중에서도 프로토콜, 도메인 주소, 포트 번호 가 같은 것을 의미합니다.
그래서 CORS로 뭐하는거에요?
다시 CORS로 돌아와서 이러한 동일 출처 정책이 있기 때문에 원래는 다른 출처에서 리소스를 받아오는 것이 제한됩니다.
하지만 다른 출처로부터 리소스를 받아오는 것은 필수 요소가 되었고, 이러한 문제를 해결하기 위해 나온 정책이 바로 CORS(교차 출처 자원 공유) 입니다.
"그렇다면 CORS는 어떻게 안전하게 다른 출처와 리소스를 공유하는 것일까요?"
바로 두 가지 방법이 있습니다.
1. 단순 요청 방법
2. 예비 요청 방법
그 전에 우리는 요청 방법에 대해서 알아야 합니다.
요청방법이 무엇인가요?
Header
- 요청 방법은 요청하는 헤더와 응답하는 헤더를 통해 이루어집니다.
요청 헤더
1. Access-Control-Request-Method
- 요청을 할 때 어떤 "메서드" 를 사용할 것인지 알려주는 것입니다.
2. Access-Control-Request-Headers
- 요청을 할 때 어떤 "헤더" 를 사용할 것인지 알려주는 것입니다.
응답 헤더
1. Access-Contorl-Allow-Origin
리소스에 접근할 수 있도록 허용하는지를 알려주는 것입니다.
2. Access-Control-Expose-Headers
브라우저에 접근할 수 있는 리스트를 알려주는 것입니다.
3. Access-Contorl-Max-Age
캐싱되는 시간을 알려주는 것입니다.
4. Access-Control-Allow-Credentials
Credentials가 true 일 때 요청에 대한 응답이 노출될 수 있는지를 나타냅니다.
5. Access-Control-Allow-Method
요청에 대한 대한 응답으로 허용되는 메서드들을 나타냅니다.
그러면 CORS는 어떻게 동작하나요?
cors 접근 제어 시나리오( 교차 출처 리소스 공유가 동작하는 방식 ) 에는 크게 세가지가 있습니다.
프리플라이트 요청 ( Preflight Request )
Preflight Request는 요청을 위에서 설명했듯이 예비 요청과 본 요청으로 나눕니다.
OPTIONS 메서드를 통해 다른 도메인의 리소스에 요청이 가능한지 ( 실제 요청이 전송하기에 안전한지 ) 확인 작업을 하고, 요청이 가능하다면 실제 요청을 보냅니다.
Cross-origin 요층을 유저 데이터에 영향을 줄 수 있기 때문에 Preflight 요청을 합니다.
클라이언트와 서버간의 첫 번째 통신인 preflight request/response 를 살펴보겠습니다.
- Preflight Request
OPTIONS 요청과 함께 두 개의 다른 요청 헤더가 전송됩니다.
아래에서 첫 행은 실제 요청을 전송할 때 POST 메서드로 전송된다는 것이고, 두 번째 행은 실제 요청을 전송 할 때 X-PINGOTHER 과 Content-Type 사용자 정의 헤더와 함께 전송된다는 것을 서버에 알려줍니다.
- Preflight Response
서버와 메서드가 헤더를 받을 수 있음을 알려줍니다.
마지막 행은 preflight request에 대한 응답을 캐시할 수 있는 시간(초) 입니다. 아래에서는 86400초(=24시간)
프리플라이트를 보내면 사전, 실제 요청 두번이 매번 왔다갔다 하므로 브라우저가 캐싱을 해두고 똑같은 요청을 보낼 때, 사전 요청을 보내지 않고 바로 본 요청을 보냅니다.
preflight request가 완료되면 실제 요청을 전송합니다.
단순 요청 ( Simple Request )
Simple Request는 Preflight Request와 다르게 요청을 보내면서 즉시 cross origin 인지 먼저 확인하는데, 다음 조건을 모두 충족해야합니다.
- 메서드는 GET, POST, HEAD 중 하나
- 헤더는 Accept, Accept-Language, Content-Language, Content-Type 만 허용
- Content-Type 헤더는 다음의 값들만 허용
- applicaton/x-www-form-urlencoded
- multipart/form-data
- text/plain
인증정보 포함 요청 ( Credentialed Request )
인증 관련 헤더를 포함할 때 사용하는 요청입니다.
브라우저가 제공하는 비동기 리소스 요청 API인 XMLHttpRequest 객체나 fetch API는 별도의 옵션 없이 브라우저의 쿠키 정보나 인증과 관련된 헤더를 기본적으로 요청에 담지 않으므로, credentials 옵션을 변경하지 않고서는 cookie를 주고 받을 수 없습니다.
옵션은 총 세가지가 있다.
- omit : 절대로 cookie 들을 전송하거나 받지 않는다.
- same-origin : 동일 출처(same origin) 이라면, user credentials (cookies, basic http auth 등..) 을 전송합니다 ( default 값 )
- include : cross-origin 호출이라 할지라도 언제나 user-credentials ( cookies, basic http auth 등..)을 전송합니다.
CORS 정책 위반 해결 방법
저는 대학교 때 백엔드부분을 담당했기에 CORS 정책 위반을 알지 못했습니다.
하지만 프론트 부분을 담당했던 친구가 CORS 오류가 난다고 해서 알게 되었습니다.
이렇듯 CORS 정책 위반은 백엔드의 문제지만 알게 되는 순간은 프론트와 통신하는 순간 입니다.
왜냐하면 출처를 비교하는 로직이 서버단에서 구현되는 것이 아니라 브라우저단에서 이루어집니다.
그래서 cors 정책을 위반하는 리소스 요청을 하더라도 서버단에서 같은 출처에서 요청만 받겠다는 설정을 따로 해둔것이 아니라면 일단 정상적으로 응답하게 됩니다.
(참고) SOP 개념도 알아두어야 합니다.
SOP, Same-Origin Policy의 약자입니다.
웹 생태계에서 다른 출처로부터 리소스 요청을 제한하는 것과 관련된 두 가지 정책이 존재합니다.
하나는 CORS이고 하나는 SOP 입니다.
SOP 는 "같은 출처에서만 리소스를 공유할 수 있다" 는 규칙을 가진 정책입니다.
그러나 웹이라는 환경에서는 다른 출처에 있는 리소스를 가져와서 사용하는 일은 매우 흔하다.
그래서 SOP 에 대한 예외 조항을 두고 이 예외에 해당하는 요청은 출처가 다르더라도 허용하기로 했는데 이것이 "CORS 정책을 지킨 리소스 요청" 이라고 할 수 있습니다.
따라서, 우리가 다른 출처로 리소스를 요청한다면 SOP 정책을 위반한 것이고,
거기에 예외 조항인 CORS 까지 지키지 않으면 아예 다른 출처의 리소스를 사용할 수 없게 되는 것입니다.
CORS 정책 위반으로 에러가 발생했을 때 해결하는 방법에 대해 알려드리겠습니다.
⚙️저는 백단을 공부했기에 백엔드 에서 해결하는 방법을 알려드리겠습니다.
"우선, CORS의 플로우에 대해 간단하게 알아보겠습니다."
요청 순서
1. GET 요청인지 POST 요청인지 파악합니다.
2. Content-Type 과 Custom HTTP Header 를 파악합니다.
3. OPTIONS 요청을 통해서 서버가 적절한 Access-Control-* 를 가졌는지 확인합니다.
4. 만약 적절한 Access-Control 을 가졌다면 실제 XHR을 트리거합니다,.
5. 적절하지 못한 Access-Control 을 가졌다면 Error 를 발생시킵니다.
코드 상으로 오류를 고치고 싶다면 밑의 블로그를 참고해주세요.
그래서 왜 CORS를 써야하나요?
보안이 제일 문제가 됩니다.
왜냐하면 저희가 서비스 하지 않고 있는 사이트에서 세션을 요청해서 세션을 획득이 가능하다면, 해당사이트는 악의적으로 저의 세션을 탈취하거나 다른 행동을 할 수 있습니다.
그래서 브라우저에서 이러한 요청을 막는 것 입니다.
피싱 사이트가 대표적인 공격사례인데 이러한 것을 막고 제가 허용한 Origin들만 요청할 수 있도록 하기 위해 필요합니다.
'CS > 네트워크' 카테고리의 다른 글
[네트워크] HTTP 와 HTTPS 란? (0) | 2023.02.04 |
---|