본문 바로가기

Network

[Network] CORS

CORS (Cross-Origin Resource Sharing)

CORS 문제는, 클라이언트가 다른 도메인(출처)에 있는 리소스를 요청할 때 발생하는 제한이다.

SOP 정책 때문에 막혔던 다른 출처로의 요청을 CORS 설정을 거치게 되면 정상적으로 요청을 보낼 수 있게 된다. 

 

동작 원리

  • 웹 브라우저는 보안상의 이유로 동일 출처 정책(SOP)을 따른다.
  • 동일 출처란 프로토콜, 호스트, 포트가 모두 동일한 경우를 말한다.
  • 클라이언트가 다른 출처의 리소스를 요청하면, 브라우저는 이를 차단한다.

SOP (Same-Origin Policy)

Origin URL - Protocol + Host + Port

  • 출처를 판단할 때는 포트 뒤에 요소들은 제외하고 프로토콜, 호스트, 포트가 같을 때 동일한 출처로 판단하게 된다.
  • SOP는 같은 출처끼리만 요청을 보낼 수 있는 것. 
  • 웹 브라우저는 보안상의 이유로 동일 출처 정책(SOP)을 따른다.
  • 동일 출처란 프로토콜, 호스트, 포트가 모두 동일한 경우를 말한다.
  • 클라이언트가 다른 출처의 리소스를 요청하면, 브라우저는 이를 차단한다.

CORS가 필요한 이유

  • 브라우저가 사용자의 데이터를 보호하기 위해 도메인 간 요청을 제한.
  • 예: http://example.com에서 http://api.example.com으로 요청 → 제한.

CORS 문제 해결 방법:

  1. 서버 설정: 서버에서 Access-Control-Allow-Origin 헤더를 추가.
  2. 프록시 사용: 클라이언트와 동일한 도메인에서 서버로 요청을 프록시.
  3. JSONP 사용(구식): <script> 태그를 통해 서버에서 JSON 데이터를 반환.

Preflight Request

  • Preflight 요청은 CORS 요청이 실제 요청을 보내기 전에 브라우저가 서버에 보내는 사전 요청이다.
  • 이 요청은 클라이언트가 서버에 보내려는 실제 요청이 안전한지 확인하기 위해 사용된다.
  • Preflight 요청은 주로 메소드와 헤더가 서버에서 처리 가능한지를 확인하는 용도로 사용된다.

Preflight 요청의 동작

  1. Preflight 요청OPTIONS 메소드로 서버에 보내진다.
  2. 서버는 해당 요청을 받고, Access-Control-Allow-Methods, Access-Control-Allow-Headers와 같은 헤더를 포함하여 응답한다.
  3. 만약 서버가 클라이언트의 요청을 허용한다고 응답하면, 브라우저는 실제 요청을 보낸다.
  4. Preflight 요청은 한 번만 발생하고, 이후 같은 요청은 브라우저에서 캐시된다. 일정 시간 동안 동일한 요청을 보낼 때는 Preflight가 발생하지 않는다.

Credentialed Request

  • Credentialed 요청은 클라이언트가 서버에 인증 정보(Credentials)를 포함하는 요청을 보내는 것이다.
  • 여기서 인증 정보란 쿠키(Cookies), HTTP 인증 헤더(Authentication Headers) 등을 의미한다.
  • 클라이언트가 서버에 쿠키나 인증 정보를 포함하여 요청을 보낼 때, 이를 Credentialed 요청이라고 힌다.

Credentialed 요청 주의점

  • Credentials 포함 요청은 기본적으로 CORS에서 허용되지 않는다. 이를 허용하려면 서버가 명시적으로 Access-Control-Allow-Credentials 헤더를 설정해야 한다.
  • 클라이언트는 withCredentials 옵션을 설정해야 쿠키나 인증 헤더를 포함한 요청을 보낼 수 있다.
  • Access-Control-Allow-Origin에 * 와일드카드 값이 있을 수 없으며, 반드시 특정 도메인(예: http://example.com)을 명시해야 한다.

Credentialed 요청의 동작

  1. 클라이언트는 withCredentials를 true로 설정하고 요청을 보낸다.
  2. 서버는 Access-Control-Allow-Credentials: true 헤더를 포함한 응답을 보낸다.
  3. 클라이언트는 서버에서 보낸 쿠키나 인증 정보를 사용할 수 있게 된다.

Spring에서 CORS 설정 방법

1. Global CORS 설정 (모든 엔드포인트에서 CORS 허용)

@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowedOrigins("http://client.com")  // 허용할 클라이언트 도메인
                .allowedMethods("GET", "POST", "PUT", "DELETE")
                .allowedHeaders("*")
                .allowCredentials(true);
    }
}
  • allowedOrigins에 특정 도메인만 설정하여, 그 도메인에서의 요청만 허용할 수 있다.
  • allowCredentials(true)를 설정하면 쿠키나 인증 헤더를 포함한 요청도 허용된다.

2. 컨트롤러에서 CORS 설정 (특정 엔드포인트에만 CORS 허용)

@RestController
@RequestMapping("/api")
@CrossOrigin(origins = "http://client.com")
public class MyController {
    
    @GetMapping("/endpoint")
    public ResponseEntity<String> getData() {
        return ResponseEntity.ok("Hello, Client!");
    }
}
@RestController
@RequestMapping("/api")
public class MyController {
    
    @CrossOrigin(origins = "http://client.com")
    @GetMapping("/endpoint")
    public ResponseEntity<String> getData() {
        return ResponseEntity.ok("Hello, Client!");
    }
}
  • @CrossOrigin 어노테이션을 사용하여 특정 엔드포인트에 대해서만 CORS를 허용할 수 있다.
  • 클래스 레벨과 메서드 레벨에서 설정할 수 있다.

@CrossOrigin 속성

 

origins: CORS 요청을 허용할 도메인 목록입니다. 여러 도메인을 콤마로 구분해서 설정할 수 있다.

  • 예: @CrossOrigin(origins = "http://example.com, http://another-domain.com")

methods: 허용할 HTTP 메소드(GET, POST, PUT, DELETE 등)를 설정할 수 있다.

  • 예: @CrossOrigin(methods = {RequestMethod.GET, RequestMethod.POST})

allowedHeaders: 클라이언트에서 허용된 요청 헤더를 설정합니다. 기본적으로 모든 헤더가 허용된다.

  • 예: @CrossOrigin(allowedHeaders = "Content-Type, Authorization")

exposedHeaders: 클라이언트가 접근할 수 있는 응답 헤더를 설정한다.

  • 예: @CrossOrigin(exposedHeaders = "Authorization")

allowCredentials: 클라이언트가 쿠키나 인증 정보를 포함한 요청을 보낼 수 있도록 할지 여부를 설정한다.

  • 예: @CrossOrigin(allowCredentials = "true")

maxAge: 클라이언트가 preflight 요청을 재사용할 수 있는 시간을 설정합니다. 이 값은 초 단위로 설정된다.

  • 예: @CrossOrigin(maxAge = 3600) (1시간 동안 preflight 요청을 캐시)

 

 


 

'Network' 카테고리의 다른 글

[Network] 프록시 서버  (0) 2024.12.17
[Network] DNS  (0) 2024.12.03
[Network] HTTP와 HTTPS  (0) 2024.11.27
[Network] HTTP 버전별 특징  (0) 2024.11.24
[Network] Stateful vs Stateless  (2) 2024.11.24