티스토리 뷰

반응형

📌 개요

프론트엔드와 백엔드가 협업하면서 cors를 해결하는 현명한 방법


📌cors의 정의

cors란 (Cross-Origin Resource Sharing)의 약자로 직역하면 교차출처 리소스 공유이다. 

좀 더 쉽게 말하면 동일한 출처가 아닌 다른 출처에서 데이터를 주고받는 것을 허용하는 정책이다.

추가 http헤더를 사용하여, 한 출처에서 실행중인 웹 애플리케이션을 다른 출처의 선택한 자원에 접근할 수 있는 권한을 부여하도록 브라우저에 알려주는 체제이다. 

그렇다면 이러한 정책이 왜 필요한것인가? 라는 것에 이유를 알기위해선 sop 즉 "동일 출처 정책"에 대해서 알아야한다.


📌SOP(Same Origin Policy) - 동일 출처 정책

동일 출처 정책은 웹 브라우저에서 보안을 강화하기 위해 동일한 출처에서만 리소스를 주고받도록 하는 정책이다. 

1. 그렇다면 왜 다른 출처를 차단시키는 걸까? 

브라우저는 사실 공격에 굉장히 취약하다. 예를 들어 어떠한 사용자가 쇼핑몰에 로그인을해서 웹사이트에서 인증 토큰을 받아왔다.

이때 누군가 의도적으로 악의적인 자바스크립트를 웹사이트에 심어놓는다면, client의 의도와는 상관없이 client로 위장해서 서버에 접근할 수 있다. 이때 해당 사이트의 개인정보와 같은 민감한 정보를 가져올 수 있기 때문에 sop를 지켜야 한다.  

이것이 origin을 통해 어디에서 요청한 url인지 확인하여 같은 출처인지 판단하고 같지 않으면 cross origin 즉 다른 출처라고 판단하여 sop 정책에 위배되어 차단되는 것이다. 

2. 동일한 출처를 판단하는 기준

 

출처는 URL의 스킴(프로토콜), 호스트(도메인), 포트 이 3가지로 판단할 수 있다.

즉 어떤 url이 같은 출처인지 판단하려면 url의 프로토콜, 호스트, 포트가 모두 같은지 확인하면 된다.

👉동일 출처 예시
- http://example.com/hellohttp://example.com/bye 는 다른 URL이지만, 프로토콜, 도메인, 포트가 모두 같으므로 동일 출처이다.
- http://example.comhttps://example.com 은 같은 리소스를 가리키고 있지만, 프로토콜이 다르므로 동일 출처가 아니다.
- http://example.comhttp://example.com:80 은 얼핏보면 다른 출처같지만, 전자의 경우 http 의 기본 포트인 80이 생략된 형태이므로 이는 같은 출처라고 할 수 있다.

3. 그렇다면 다른 출처의 리소스가 필요하다면 어떻게 해야할까?

 cors허용을 해준다. 


📌cors의 접근제어 시나리오.

1. 프리플라이트 요청(preflight request)
2. 단순 요청(simple request)
3.인증정보 포함 요청(Credentialed request)

1. 단순 요청(simple request)

단순 요청은 특정 조건이 만족되면 프리플라이트 요청을 생략하고 요청을 보내는 것을 의미한다.

조건은 다음과 같다.

ㄱ.  get, post,head 메서드 중의 하나여야한다.
ㄴ. Content-Type 헤더에는 application/x-www-form-urlencoded, multipart/form-data, text/plain 값만 허용된다.
ㄷ.헤더는 자동으로 설정되는 헤더 외에 Accept, Accept-Language, Content-Language, Content-Type만 허용된다. 

2. 프리플라이트 요청(preflight request)

프리플라이트는 쉽게 말하자면 사전 확인작업이다.

Preflight 있는가에 대해서는 쉽게 말해서 CORS spec 생기기 이전에 만들어진 서버들은 브라우저의 SOP(same origin policy) request 가능하다는 가정하에 만들어졌는데, cross-site request CORS 인해서 가능해졌기 떄문에 이런 서버들은 cross-site request 대한 security mechanism 없다보니 보안적으로 문제가 생길수 있으니 이런 서버들을 보호하기 위해 CORS spec preflight request 포함했다. Preflight request 서버가 CORS 인식하고 핸들할수있는지 먼저 확인을 함으로써 CORS 인식하지 못하는 서버들을 보호할수있는 매커니즘 이라는 것이다.

프리플라이트 요청은 2번 요청을 진행한다.

첫번째로, options 메소드를 통해 다른 도메인의 리소스에 요청이 가능한지 확인한다. 서버에게 프리플라이트의 options를 통해 서버 통신이 가능한지 미리 확인하는 것이다. 그 후 요청이 가능하다면 실제 요청(actual request)를 보낸다.

 

위 이미지의 흐름과 같이, 브라우저는 서버에 실제 요청을 보내기 전에 플리플라이트 요청을 보내고, 응답 헤더의 Allow-Control-Allow-Origin 값으로 요청을 보낸 출처가 돌아오면 실제 요청을 보내게 된다.

 

만약에 요청을 보낸 출처가 접근 권한이 없다면, 응답 헤더에 Allow-Control-Allow-Origin이 아예 들어있지 않는다.

그 경우 브라우저에서 CORS 에러를 띄우게 되고, 실제 요청은 전달되지 않는다.

⭐️프리플라이트 요청은 왜 필요한 걸까?

  • CORS를 모르는 서버를 위해서이다.
  • 실제 요청을 보내기 전에 미리 권한 확인을 할 수 있기에, 실제 요청은 처음부터 통째로 보내는 것보다 리소스 측면에서 효율적이다.
  • CORS에 대비가 되어있지 않은 서버를 보호할 수 있다. CORS 이전에 만들어진 서버들은 SOP 요청만 들어오는 상황을 고려하고 만들어졌다. 따라서 다른 출처에서 들어오는 요청에 대한 대비가 되어있지 않았다.

이런 서버에 바로 요청을 보내면, 응답을 보내기 전에 우선 요청을 처리하게 된다. 브라우저는 응답을 받은 후에 CORS 권한이 없다는 것을 인지하지만, 브라우저가 에러를 띄운 후에는 이미 요청이 수행된 상태가 된다. 만약에 들어온 요청이 DELETE나 PUT처럼 서버의 정보를 삭제하거나 수정하는 요청이었다면? 이미 서버에서는 delete를 한 후여서 cors권한이 없음을 뒤늦게 확인한 상태이므로 이미 이를 막을 수 있는 방법이 없다. 

하지만 CORS에 대비가 되어있지 않은 서버라도 플리플라이트 요청을 먼저 보내게 되면, 플리플라이트 요청에서 CORS에러를 띄우게된다. 예시와 같이 실행되서는 안되는 Cross-Origin 요청이 실행되는 것을 방지할 수 있는 것이다. 이러한 이유로 플리플라이트 요청이 기본 사양으로 들어가게 되었다!

3.인증정보를 포함한 요청 (Credentialed Request)

인증 관련 헤더를 포함할 때 사용하는 요청으로 요청 헤더에 인증 정보를 담아 보내는 방식이다.

출처가 다를 경우에는 별도의 설정을 하지 않으면 쿠키를 보낼 수 없다. 왜냐하면 민감한 정보이기 때문이다! 이 경우에는 클라이언트측, 서버 양측 모두 CORS 설정이 필요하다.

  • 클라이언트 측에서는 요청 헤더에 withCredentials : true를 넣어주어야 한다.
  • 서버 측에서는 응답 헤더에 Access-Control-Allow-Credentials : true 를 넣어줘야한다.
    단 (Access-Control-Allow-Origin : * ) 을 하면 에러가 발생한다. 모든 출처를 허용한다는 뜻이므로 정확한 출처만 허용해주어야한다.

📌CORS 설정 방법

1. 프론트에 프록시 서버 설정(개발환경)
2.직접 헤더에 설정해주기
3. 스프링 부트 이용하기 

1. 프론트에 프록시 서버 설정(개발환경)

먼저 http-proxy-middleware를 설치

npm install http-proxy-middleware --save

설치를 하고 나서 src/setupProxy.js 파일을 만들어서 내용을 작성해달라고 합니다.

const { createProxyMiddleware } = require('http-proxy-middleware');

module.exports = function(app) {
  app.use(
    '/api',
    createProxyMiddleware({
      target: 'http://localhost:5000',
      changeOrigin: true,
    })
  );
};

프록시 설정을 했기 때문에 랜딩페이지에서 axios부분에 http://localhost:5000은 지워줍니다!

그러고 다시 실행시키면 CORS에러가 사라진다.

3. 스프링 부트 이용하기 

ㄱ.Configuration으로 해결하기

전역으로 cors 적용해주는 방법이다. 우선 config 패키지를 만들어 준다.
경로는  /src/main/java/{project}/config 

만들어진 config 패키지 안에 webConfig 클래스를 만들어준다.

@Configuration
public class WebConfig implements WebMvcConfigurer {
}

클래스 상단에 @Configuration 어노테이션을 통해 설정파일이라는 것을 알려준다.

그리고 WebMvcConfigurer를 implements한다.

@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void addCorsMappings(CorsRegistry registry) {
    }
}

그 다음 addCorsMappings메소드를 오버라이드 한다고 하는데 자세한건 아래 링크 참고.

https://dev-pengun.tistory.com/entry/Spring-Boot-CORS-%EC%84%A4%EC%A0%95%ED%95%98%EA%B8%B0

반응형
반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/09   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30
글 보관함