Network Basis

[Network] SOP 와 CORS Policy

DevPing9_ 2021. 12. 1. 14:57

CSRFXSS 같은 방법을 사용하여, 어플리케이션에서 사용자의 정보를 탈취하는 것을 막기 위한 정책들

 

브라우저 단에서 적용되는 정책이다.

 

서버간의 통신에는 적용되지 않는다.

 

 

# SOP(Same-Origin Policy)

  • '같은 출처에서만 리소스를 공유할 수 있다' 라는 개념의 정책

 

# CORS(Cross-Origin Resource Sharing) Policy

  • 다른 출처로 리소스를 요청할때 지켜야하는 SOP가 허용하는 예외 조항

 

# 같은 출처와 다른 출처의 판단

ex) https://www.naver.com:8080           

Scheme : https://

Host : www.naver.com    

Port : :8080

  • 기본적으로 Scheme, Host, Port 가 동일해야 같은 출처 (RFC 6454)
  • 브라우저에 따라 Scheme, Host 만 비교하는 브라우저도 있다. (Intenet Explorer...)
  • 브라우저에 따라 비교 로직이 다르다.
  • 일단 서버에서 Response를 받지만, 정책위반(CORS,SOP)시 브라우저에서 Response를 파기
  • 즉, http://localhost:3000http://localhost:8080 는 포트번호가 다르므로 다른 출처가 되고, 3000 포트에서 8080 포트로 리소스를 요청한다면 다른출처로 요청한다고 판단하여 CORS 정책을 실시하게 된다.
  • Origin = Scheme + Host + Port

 

 

# CORS 의 기본적인 동작방식

  • 기본적으로 클라이언트에서 다른 출처로 리소스를 요청할 때, 브라우저request 헤더의 'Origin' 이라는 필드에 본인 origin 을 담아 보낸다.
  • 서버가 Response 를 보낼 때, Response 헤더에 'Access-Control-Allow-Origin' 필드에 서버가 어디까지 허용하는지 허용된 URL을 담아 보낸다.
  • Response를 받은 브라우저는 'Origin' 과 'Access-Control-Allow-Origin'을 비교 후, 유효한 응답인지 결정한다.

 

# CORS 의 기본정책을 지키는 코드 예시

// Client (React App)

//서버에 Request (to fetch list of user)
//React App 은 http://localhost:3030 으로 가정
axios.get("http://localhost:8080/api/user") 
    .then((res)=>console.log(res)).catch().finally();
    
/* 요청을 보낼때 {"Origin":"http://localhost:3030"} 을 자동으로 헤더에 실어 같이 보낸다. */
// Serever (Spring App)

@GetMapping("/api/user")
public Res<User> getUsers(){
	/* ...get List of User from DB */
    
    HttpHeaders httpHeaders = new HttpHeaders();
    
    httpHeaders.set("Access-Control-Allow-Origin","*"); // "*" 은 모든 곳에서의 요청을 허용하겠다는 의미
    httpHeaders.set("Access-Control-Allow-Methods","GET"); // 허용하는 HTTP 메소드
    httpHeaders.set("Access-Control-Allow-Headers","Content-Type, Authorization, Content-Length, X-Requested-With");
    
	return res;
}
    
/* 응답을 보낼때 
  {"Access-Control-Allow-Origin":"허용범위"} 를 
  꼭 헤더에 실어 같이 보내야 CORS 정책을 위반하지 않는다. */
  
/* The CORS spec 은 all-or-nothing 이므로
   {"Access-Control-Allow-Origin" : "*://*.mysite.*"} 같은 표현은 먹히지 않는다.
   정규식으로 Origin 도메인이 허용범위에 있는지 확인 후 요청온 Origin 도메인을 넣어주어야 한다.

*/

 

# 서버로 실어보낸 헤더 내용

브라우저가 아닌 Talent API 를 통해 요청을 보냈을 때는 Origin 헤더가 요청에 담기지 않는다.

좌) React(localhost:3030)에서 서버에 요청할때의 헤더내용 // 우)Talent API(localhost:9090)에서 서버에 요청할때 헤더내용

 


위의 코드에서 보았듯이,

'Access-Control-Allow-Origin' 필드뿐 아니라

CORS 위한 다른 필드들이 있음을 짐작할 수 있다.

 

 

# CORS 심층분석 (3가지 시나리오)

 

Scenario 1. (PreFlight Request)

 

Scenario2, 3 이 아닌 모든 상황(Default)일때, 브라우저는 요청을 한번에 보내지 않고 예비요청과 본요청으로 나누어서 서버로 전송한다.

 

예비요청(PreFlight)은 HTTP 메소드의 OPTIONS 메소드를 사용한다.

예비요청의 역할은 본 요청을 보내기 전에 브라우저 스스로 이 요청이 안전한지 확인하는 것이다.

 

서버는 예비요청에 대한 응답으로, 자신이 어떤것들을 허용하고 금지하는지에 대한 정보를 헤더에 담아 보내준다. 

 

* 매번 PreFlight를 날리는 것은 비효율적이기에, 서버 설정을 통해 PreFlight 의 결과의 캐시를 일정 기간 동안 저장 시킬 수 있다.

* 서버측에서 Access-Control-Max-Age 헤더에 값을 담아 캐시의 지속시간을 설정한다.


Scenario 2. (Simple Request)

 

예비요청없이 본요청을 바로 때려박는다.

 

다음 조건을 모두 충족해야 simple request 가 실행된다.

 


Scenario 3. (Credentialed Request)

다른 출처 간 통신에서 좀 더 보안을 강화하고 싶을 때 사용하는 방법

 

request 헤더'crendeitals' : [OPTION] 을 담아 보낸다.

 

사용가능한 OPTION 은 다음 3가지 이다.

 

1. 'same-origin' (default option) 

  • 같은 출처 간 요청에만 인증정보를 담을 수 있다

2. 'include'

  • 모든 요청에 인증정보를 담을 수 있다.

3. 'omit'

  • 모든 요청에 인증정보를 담지 않는다.

 

# 코드 예시 (Backend 쪽은 추후, 업데이트 하겠음)

// Client (React App)

//서버에 Request (to fetch list of user)
axios.get("http://localhost:8080/api/user",{withCredentials:true}) 
    .then((res)=>console.log(res)).catch().finally();

 

# Reference

* CORS에 대해 매우 잘 정리 해주신 글

 

CORS는 왜 이렇게 우리를 힘들게 하는걸까?

이번 포스팅에서는 웹 개발자라면 한번쯤은 얻어맞아 봤을 법한 정책에 대한 이야기를 해보려고 한다. 사실 웹 개발을 하다보면 CORS 정책 위반으로 인해 에러가 발생하는 상황은 굉장히 흔해서

evan-moon.github.io

* CORS (WEB MDN)

 

교차 출처 리소스 공유 (CORS) - HTTP | MDN

교차 출처 리소스 공유(Cross-Origin Resource Sharing, CORS)는 추가 HTTP 헤더를 사용하여, 한 출처에서 실행 중인 웹 애플리케이션이 다른 출처의 선택한 자원에 접근할 수 있는 권한을 부여하도록 브라

developer.mozilla.org

* CORS Spec is All or Nothing 

 

Access-Control-Allow-Origin wildcard subdomains, ports and protocols

I'm trying to enable CORS for all subdomains, ports and protocol. For example, I want to be able to run an XHR request from http://sub.mywebsite.example:8080/ to https://www.mywebsite.example/* Typ...

stackoverflow.com

 

728x90