CORS (교차 출처 리소스 공유)

2023. 1. 5. 23:41기술 창고/정보 창고

728x90
SMALL

CORS(Cross-Origin Resource Sharing)는 교차 출처 리소스 공유 정책이라고 해석할 수 있다.

즉 다른 출처의 자원에 관한 공유 정책이라고 보면 될 것이다.

 

[CORS 에러 예시]

🚨 Access to fetch at ‘https://myhompage.com’ from origin ‘http://localhost:3000’ 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.

💬 'https://myhomepage.com'에서 'https://localhost:3000' 출처로 가져올 수 있는 액세스가 CORS 정책에 의해 차단되었습니다. 요청된 리소스에 'Access-Control-Allow-Origin' 헤더가 없습니다. 불투명한 응답이 필요에 적합한 경우, 요청 모드를 'no-cors'로 설정하여 CORS가 비활성화된 리소스를 가져오십시오.

 

해석한 내용처럼 CORS 정책으로 인해 localhost:3000 주소의 자원을 가져오는 데에 있어서 차단이 된 것이다.

 


 

여기서 중요 키워드인 출처를 알아보자.

 

출처(ORIGIN)

어떠한 사이트를 접속할 때 인터넷 주소에 URL이라는 문자열을 통해서 접근하게 되는데

URL은  https://domain.com:3000/user?query=name&page=1 와 같은 문자열 형태로 이루어져있다.

 

URL의 부분마다 의미하는 것이 다른데,

 

- https:// : Protocol (프로토콜)- domain.com : Host (사이트 도메인)- :3000 : Port (포트번호)- /user : Path (경로)- ?query=name&page=1 : Query String (요청된 key 와 value 값)

 

로 나눌 수 있다.

여기서 Origin이 의미하는 부분은 프로토콜, 호스트, 포트까지의 문자열 URL을 의미한다.

 

프로토콜이나 호스트, 포트번호 중 하나라도 다르다면 다른 출처의 자원을 공유하게 되는 것이다.

 

 

 

CORS 동작

1. 클라이언트에서 HTTP요청의 헤더에 Origin을 담아 전달

  1. 기본적으로 웹은 HTTP 프로토콜을 이용하여 서버에 요청을 보내게 되는데,
  2. 이때 브라우저는 요청 헤더에 Origin 이라는 필드에 출처를 함께 담아 보내게 된다.

 

2. 서버는 응답헤더에 Access-Control-Allow-Origin을 담아 클라이언트로 전달한다.

  1. 이후 서버가 이 요청에 대한 응답을 할 때 응답 헤더에 Access-Control-Allow-Origin이라는 필드를 추가하고 값으로 '이 리소스를 접근하는 것이 허용된 출처'를 내려보낸다.

 

3. 클라이언트에서 자신이 보냈던 요청의 Origin과 서버가 보내준 Access-Control-Allow-Origin을 비교한다.

  1. 이후 응답을 받은 브라우저는 자신이 보냈던 요청의 Origin과 서버가 보내준 응답의 Access-Control-Allow-Origin을 비교해본 후 차단할지 말지를 결정한다.
  2. 만약 유효하지 않다면 그 응답을 사용하지 않고 버린다. (CORS 에러 !!)
  3. 위의 경우에는 둘다 http://localhost:3000이기 때문에 유효하니 다른 출처의 리소스를 문제없이 가져오게 된다.

 

 

CORS 해결책 (허용방법)

1. Chrome 확장 프로그램 이용

Chrome에서는 CORS 문제를 해결하기 위한 확장 프로그램을 제공해준다. 

아래 링크에서 'Allow CORS: Access-Control-Allow-Origin' 크롬 확장 프로그램을을 설치 해준다.

 chrome.google.com
Easily add (Access-Control-Allow-Origin: *) rule to the response header.

그러면 브라우저 오른쪽 상단에서 확장 프로그램을 활성화 시킬 수 있다. 해당 프로그램을 활성화 시키게 되면, 로컬(localhost) 환경에서 API를 테스트 시, CORS 문제를 해결할 수 있다.

 

 

2. 프록시 사이트 이용하기

프록시(Proxy)란 클라이언트와 서버 사이의 중계 대리점이라고 보면 된다.

즉, 프론트에서 직접 서버에 리소스를 요청을 했더니 서버에서 따로 설정을 안해줘서 CORS 에러가 뜬다면, 모든 출처를 허용한 서버 대리점을 통해 요청을 하면 되는 것이다.

다만 현재 무료 프록시 서버 대여 서비스들은 모두 악용 사례 때문에 api 요청 횟수 제한을 두어 실전에서는 사용하기 무리이다. 따라서 테스트용이나 맛보기용으로 사용하되, 실전에서는 직접 프록시 서버를 구축하여 사용하여야 한다.

 

 

3. 서버에서 Access-Control-Allow-Origin 헤더 세팅하기

직접 서버에서 HTTP 헤더 설정을 통해 출처를 허용하게 설정하는 가장 정석적인 해결책이다.

CORS에 연관된 HTTP 헤더 값으로는 다음 종류가 있다. (이들을 모두 설정할 필요는 없다)

# 헤더에 작성된 출처만 브라우저가 리소스를 접근할 수 있도록 허용함.
# * 이면 모든 곳에 공개되어 있음을 의미한다. 
Access-Control-Allow-Origin : https://naver.com
 
# 리소스 접근을 허용하는 HTTP 메서드를 지정해 주는 헤더
Access-Control-Request-Methods : GET, POST, PUT, DELETE
 
# 요청을 허용하는 해더.
Access-Control-Allow-Headers : Origin,Accept,X-Requested-With,Content-Type,Access-Control-Request-Method,Access-Control-Request-Headers,Authorization
 
# 클라이언트에서 preflight 의 요청 결과를 저장할 기간을 지정
# 60초 동안 preflight 요청을 캐시하는 설정으로, 첫 요청 이후 60초 동안은 OPTIONS 메소드를 사용하는 예비 요청을 보내지 않는다.
Access-Control-Max-Age : 60
 
# 클라이언트 요청이 쿠키를 통해서 자격 증명을 해야 하는 경우에 true. 
# 자바스크립트 요청에서 credentials가 include일 때 요청에 대한 응답을 할 수 있는지를 나타낸다.
Access-Control-Allow-Credentials : true
 
# 기본적으로 브라우저에게 노출이 되지 않지만, 브라우저 측에서 접근할 수 있게 허용해주는 헤더를 지정
Access-Control-Expose-Headers : Content-Length

 

서버에서 직접 Access-Control-Allow-Origin 헤더를 세팅하는 것은 사용하는 서버에 따라 다르다.

나는 Spring을 주로 다루었기 때문에 Spring에 대한 세팅을 다루자면,

// 스프링 서버 전역적으로 CORS 설정
@Configuration
public class CorsConfig implements WebMvcConfigurer {
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
        	.allowedOrigins("http://localhost:8080", "http://localhost:8081") // 허용할 출처
            .allowedMethods("GET", "POST") // 허용할 HTTP method
            .allowCredentials(true) // 쿠키 인증 요청 허용
            .maxAge(3000) // 원하는 시간만큼 pre-flight 리퀘스트를 캐싱
    }
}

CorsConfig 라는 Access-Control-Allow-Origin 헤더를 세팅하는 설정 클래스를 만들어준다.

localhost:8080 , localhost:8081 출처에 대해서 GET, POST 방식의 요청을 허용하겠다는 뜻이다.

// 특정 컨트롤러에만 CORS 적용하고 싶을때.
@Controller
@CrossOrigin(origins = "*", methods = RequestMethod.GET) 
public class customController {
 
	// 특정 메소드에만 CORS 적용 가능
    @GetMapping("/url")  
    @CrossOrigin(origins = "*", methods = RequestMethod.GET) 
    @ResponseBody
    public List<Object> findAll(){
        return service.getAll();
    }
}

해당 컨트롤러에서만 CORS를 적용할 수도 있다.

@CrossOrigin으로 CORS를 적용한다.(아무 출처나 허용, method는 GET 방식)

 

 

최근에 진행했던 프로젝트에서는 조금 다른 방식으로 CORS를 허용하였다.

 

@Slf4j
@Configuration
public class CorsConfig {
    @Bean
    public CorsFilter corsFilter(){
        UrlBasedCorsConfigurationSource source =new UrlBasedCorsConfigurationSource();
        CorsConfiguration config = new CorsConfiguration();
        config.setAllowCredentials(true);// 내서버가 응답을 할 떄 json을 자바스크립트에서 처리할 수 있게 할지를 설정하는 것
        config.addAllowedOrigin("https://haetae.shop/");
        config.addAllowedOrigin("https://haetae.shop/5000");
        config.addAllowedOrigin("https://localhost:5000");
        config.addAllowedOrigin("https://localhost:8443");
        config.addAllowedOrigin("https://cheiks.shop/");
        config.addAllowedOrigin("http://localhost:3000");
        config.addAllowedOrigin("https://www.liergame.link/");
        config.addAllowedOriginPattern("*");//모든 ip의 응답을 허용하겠다. // 게시글 테스트 떄문에 잠시 열어둠
        config.addAllowedHeader("*");//모든 header의 응답을 허용하겠다.
        config.addAllowedMethod("*");//모든 post,get,putmdelete,patch 요청울 허용하겠다.
        config.addExposedHeader("*");
        source.registerCorsConfiguration("/lier/**",config);

        log.info("회원관리 기능 절차(jwt) -> CorsConfig - corsFilter 메소드");

        return new CorsFilter(source);
    }
}

CorsConfig 와 관련된 설정 클래스를 마찬가지로 만들어준다.

CorsConfiguration 객체를 사용하여 설정값을 정해주었다.

- setAllowCredentials(true) : 내 서버가 응답을 하게 되어 json을 넘기게 되면 javascrpit에서 처리할 수 있도록 하는 Cors설정이다.

- addAllowedOrigin(~) : 허용하고자 하는 출처의 ip를 기입한다. 즉, 모든 ip,url에 대해 응답을 허용하게 된다.

- addAllowedHeader(~) : 어떠한 헤더에 대한 응답을 허용하겠다는 뜻이다.

- addAllowedMethod(~) : 어떠한 요청 방식을 허용하겠다는 뜻이다.

 

@Bean
@Order(SecurityProperties.BASIC_AUTH_ORDER)
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
       
    http.csrf().disable()
            .addFilter(corsConfig.corsFilter())
            .apply(new JwtSecurityConfiguration(SECRET_KEY, tokenProvider, userDetailsService));

    return http.build();
}

프로젝트에 Spring Security를 적용하여 만들었기 때문에 SecurityConfiguration에서 addFilter로 만들어놓은 CORS 설정을 등록해준다.

 

 


보안 취약점 여지..

CORS를 허용하게 된다면 다른 출처에 접근하여 자원을 공유할 수 있지만, 억지로 접근 권한을 뚫어주는 것이나 다름없기에 해킹과 같은 보안 위협이 있는 것은 당연하다.

또한, CORS 설정을 진행하면서 아무렇지 않게 와일드 카드(*)로 설정하게 된다면 무방비한 상태로 그대로 노출되기 떄문에 명확한 출처들만 허용할 수 있도록 지정하는 것이 중요하다.

728x90
반응형
LIST