CORS : Cross-Origin Resource Sharing

uoayop·2021년 12월 13일
1

WEB

목록 보기
3/8
post-thumbnail

생각만 해도 눈물이 나는 CORS 에러..

다들 한번쯤은 봤을 것이다.,.,

💀 Access to fetch at ‘https://localhost/post’ from origin ‘http://localhost:8080’ has been blocked by CORS policy: No ‘Access-Control-Allow-Origin’ header is present on the requested resource. If an opaque response serves your needs, set the request’s mode to ‘no-cors’ to fetch the resource with CORS disabled.

이 에러를 처음 접했을 때 해결하려고 찾아보니까,
에러 메세지엔 헤더에 Access-Control 어쩌구를 넣으라는데
그게 뭔데요 ㅠ ㅠ,,

검색을 통해 이해해보려고 했지만 ... 도대체 무슨 말인지 도통 알 수가 없었다. ^_ㅠ

요청 출처가 다르면 발생하는 에러라고 두루뭉술하게 이해하고 있었는데
이번 기회에 확실하게 정리해보았다.

🌈 우아한 Tech 테코톡
🌳 나봄님의 CORS 영상을 보며 작성한 글입니다.

  • 진짜 세상에서 제일 쉽게 설명해주시니까 꼬옥 영상으로 보시길

SOP

  • CORS를 알기 전에 SOP 개념을 먼저 짚고 가야한다.

SOP 🤔

  • Same Origin Policy
    다른 출처의 리소스를 사용하는 것에 제한을 두는 보안 방식을 의미한다.
    즉, 출처가 같을 때만 리소스를 주고 받을 수 있도록 하는 정책이다.

출처 (Origin)

  • 출처란 URL의 프로토콜, 호스트, 포트 로 정의된다.
  • 세가지가 같으면 같은 출처,
    세가지 중 하나라도 다르면 다른 출처라고 판단하면 된다.
    • 단 IE의 경우 포트번호를 출처로 고려하지 않는다.


출처: 🌳 나봄님의 CORS

  • 그림에서 프로토콜은 https, 호스트는 github.com, 포트는 :443이다.
    다음 중 http://localhost 와 동일 출처인 url은 몇 번일까 🧐
    1. https://localhost
    2. http://localhost:80
    3. http://127.0.0.1
    4. http://localhost/api/cors

1번은 프로토콜이 다르니까 다른 출처,
2번은 생략된 80번 포트번호를 적은 url이니까 동일 출처,
3번은 호스트가 다르니 다른 출처,
4번은 같은 출처에 path를 덧붙인 경우니까 동일 출처!

SOP 를 사용하는 이유


출처: 🌳 나봄님의 CORS

아래의 상황을 가정해보자
1. 페이스북 서비스를 이용하는 유저가 로그인을 해서 유저 인증 토큰을 받아왔다.
2. 해커가 유저에게 자신이 만든 사이트로 이동하게끔 하는 메일을 보낸다.
3. 해커가 만든 사이트엔 페이스북에 글을 작성하게 하는 스크립트가 담겨있다.
4. 유저가 메일을 클릭하면?

  • 유저가 메일을 클릭하면 해커가 만든 사이트로 이동하게 되고,
    해커는 유저의 인증 토큰을 사용해서 페이스북에 글을 작성하도록 스크립트를 실행시킨다.

  • 이 때 페이스북은 서비스 요청이 들어온 출처를 확인해본다.

  • 페이스북은 해당 출처가 자신의 출처와 다르기 때문에,
    Cross Origin에 해당하기 때문에 SOP에 위반된다고 판단한다!

CORS의 등장 ☄️

  • SOP가 보안 상 유리한 건 알겠다.
    근데 다른 출처의 리소스가 필요한 경우는 어떻게 하면 좋을까?
    이 때 CORS가 쓰이게 된다.

CORS

  • Cross-Origin Resource Sharing
    추가 HTTP 헤더를 사용해 한 출처에서 실행중인 웹 어플리케이션이 다른 출처의 자원에 접근할 수 있는 권한을 브라우저에게 알려주는 체제이다.
  • 다른 출처에 있는 자원에 접근 + 접근 권한을 브라우저에게 알려준다는 것에 초점을 맞추자

CORS 언제 발생하나요?

  • CORS 접근 제어 시나리오는 총 세가지가 있다.
    • 단순 요청 : Simple Request
    • 프리플라이트 요청 : Preflight Request
    • 인증정보 포함 요청 : Credentialed Request

1. Preflight Request

본 요청을 보내기 전에 서버에게 요청을 보내도 되는지 확인 받는 작업을 프리플라이트라고 한다.
이 때 OPTIONS 메소드를 통해 요청을 보낸다.
만약 사전 요청에서 가능하다는 응답을 받으면, 그 때 본 요청을 보낸다.

프로젝트를 하면서 도대체 프리플라이트가 뭔데 요청하지도 않은 OPTIONS 메소드가 뜨는지 정말정말 궁금했다

  • 해당 영상에 덧붙여져 설명이 잘 되어있는 댓글을 함께 가져왔다.


출처: 🌳 나봄님의 CORS

  • 프리플라이트 한 줄 요약 = 사전 확인 작업

  • 프리플라이트 세 줄 요약

  1. CORS 스펙이 등장하기 전에 만들어진 서버는 SOP 요청만 가능했음
  2. CORS가 나오면서 다른 출처 간에 리소스 공유가 가능해짐
  3. SOP밖에 모르는 서버들은 보안적으로 취약하니까
    "너 CORS 아니? 대비해놨니?" 라고 물어보는 매커니즘
    == 프리플라이트
  • 사전 확인이 필요한 이유
    • 만약 CORS에 대해 대비하지 않은 서버인 경우엔
      사용자가 나쁜 마음을 먹고 요상한 요청을 보낼 수도 있다.
      (ex. 💀 : 서버의 모든 데이터를 지워줘~)
    • 서버는 한치의 의심도 없이 해당 요청을 실행한 뒤 브라우저에게 응답을 보내게 된다.
    • 브라우저가 출처가 다른 걸 인지하고 한박자 늦게 CORS 에러를 터뜨리지만,,
      이미 서버의 모든 데이터는 지워졌을 것이다.


출처: 🌳 나봄님의 CORS

  • 그래서 사전 확인 요청을 보내 서버에 CORS에 대한 대비가 있는지 확인을 하고,
    그렇지 못하다면 CORS 에러를 터뜨려 본 요청을 하지 못하게 하는 것이다.
  • 프리플라이트 응답
    • 서버에서 주는 응답 코드는 200번대인 것이 좋다.
    • 응답 바디엔 아무것도 없는 것이 좋다.
    • 응답 헤더에는 응답 캐시 시간이 담겨 있어서,
      브라우저는 해당 시간만큼 캐싱을 해둔다.
      다음 요청을 보낼 때 캐싱이 되어있는지 확인을 해서
      프리플라이트 재요청을 하지 않게끔 한다.

2. Simple Request

사전 요청없이 바로 본 요청을 보내면서, 그 즉시 Cross-Origin인지 확인하는 절차다.

  • 사전 요청 작업이 없기 때문에 서버에서 따로 작업을 해주어야 CORS 에러가 발생하지 않는다.
  • 해당 리소스에 대해 요청 서버가 접근을 해도 되는지 판별을 한 뒤
    응답 헤더
    Access-Control-Allow-Origin = '요청 도메인' 을 담아 브라우저에게 허용했다고 알려야 한다.

Simple request는 아래의 조건을 만족해야 한다.

  • 메소드 : get, post, head 중 하나
  • 커스텀 헤더가 아니여야 함 == 다음의 값 중 하나:
    Accept, Accept-Language,
    Content-Language, Content-Type
  • Content-Type 헤더 값은 다음 중 하나 :
    application/x-www-form-urlencoded,
    multipart/form-data,
    text/plain

3. Credentialed Request

인증 관련 헤더를 포함할 때 사용하는 요청으로
쿠키나 JWT 같은 토큰을 클라이언트에서 서버로 보내고 싶을 때 사용하는 요청이다.

이 요청을 하려면 아래의 설정을 해줘야 한다.

  • 클라이언트
    credentials : include
  • 서버
    Access-Control-Allow-Credentials : true
    (🔥 주의 : Access-Control-Allow-Origin: * 은 안됨!
    Credentials에 대한 값을 true로 주는 순간,
    정확한 Origin을 설정해야 에러가 발생하지 않음)

CORS 해결 방법

위에서 어떤 상황에 CORS 에러가 발생하는지 알았으니 해결방법을 알아보자

0. CORS ..끄기

  • 보안은 고려하지 않고 작업을 하고싶을 때 사용하자.
    (에러 해결보단 에러 흐린 눈 하기에 가깝다)
  • 크롬 확장 프로그램 Allow CORS를 설치한 뒤, 설정을 토글시키면 에러가 감쪽같이 사라진다.
  • 간단한 토이 프로젝트라면 모르지만 CORS 에러를 회피하는게 좋은 선택은 아니다ㅠㅡㅠ

1. 프록시 설정


출처: 🌳 나봄님의 CORS

  • 클라이언트가 백엔드 서버로 바로 요청을 보내지 않고,
    중계 서버를 한번 거친다고 생각하면 된다.
  • 클라이언트가 프론트 서버와 같은 포트로 요청을 보내면
    브라우저가 같은 출처로 인식해서 CORS가 발생하지 않는다.
    그리고 프론트 서버에서는 해당 요청에 따로 api가 덧붙여져 있는 경우엔, 백엔드 서버로 한번 더 요청을 보내면 된다.
  • CORS는 브라우저에 대한 보안 정책이기 때문에
    서버 간의 출처가 다른 경우는 고려하지 않는다!
// vue.config.js
module.exports = {
   devServer: {
      Proxy: {/api’ : {
            target: ‘http://localhost:8080',
            changeOrigin: true,
          }
      }
    }
}

2. 라이브러리 사용

  • 위와 동일하지만 라이브러리를 사용할 수도 있다. http-proxy-middleware 라이브러리를 사용하면 된다.
  • setupProxy.js 파일을 만들어 특정 경로에선 원하는 서버의 주소로 프록싱하게 해줄 수 있다.
    다른 분이 작성하신 코드를 가져왔다.
Const {createProxyMiddleware} = require(“http-proxy-middleware”)

module.exports = function(app) {
  app.use(/api”, createProxyMiddleware({
            Target: “http://localhost:5000,
            changeOrigin: true,
        })
   )
}

출처 : [나를 너무 힘들게 했던 CORS 에러 해결하기]

  • 위의 코드는 "~~/api"로 요청이 들어오면 localhost:5000/으로 프록싱 해준다는 의미다.

3. 서버에서 헤더 설정하기

  • 응답 헤더에 Access-Control-Allow-Origin = '요청 도메인' 코드를 작성해 보내는 방법이다.
  • 가장 직관적이지만 조금,, 번거로운 방법이다.

4. 스프링부트 이용하기

  1. 컨트롤러마다 @CrossOrigin 어노테이션 설정
    • @CrossOrigin(origins = (허가할 도메인))
    • 이 방법은 컨트롤러가 많은 경우엔 조금 번거롭다.
  1. 전역으로 설정하기
  • 아래의 방법으로 원하는 api에 접근 허가 할 도메인을 매핑할 수 있다.

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class CorsConfiguration implements WebMvcConfigurer{

	@Override
	public void addCorsMappings(CorsRegistry registry) {
		registry.addMapping("/api")
		.allowedOrigins("허가할 도메인");
	}
}

CORS 때문에 날린 시간만 합쳐도 이틀은 될 것 같다.
그동안 머리 속에 엉켜있었던 지식을 풀어서 쓰니 정리가 확실하게 된 것 같다.
이제는 CORS 에러가 발생해도 문제 없다!!! ㉻㉻

추후에 또 좋은 방법을 찾게 되면 계속 수정하면서 추가해봐야 겠다.


Reference 🌈

  1. [테코톡 : 나봄의 CORS]
  2. [나를 너무 힘들게 했던 CORS 에러 해결하기]
  3. [[Web] CORS 동작 방식과 해결 방법]
profile
slow and steady wins the race 🐢

0개의 댓글