CORS란 ? 해결책은 ?

구범모·2023년 10월 21일
0
post-thumbnail

CORS (Cross Origin Resource Sharing)

  • 현재 Ip가 아닌 다른 Ip로 리소스를 요청하는 구조
  • Cross Origin : 클라이언트 서버와 요청 대상 서버의 IP가 다른 경우 (Cross를 교차가 아닌, 다른으로 해석하는 것이 더 이해가 편하다.)
  • Origin을 구분하는 방법
    • 스키마, host, port 3가지로 구성된다.
    • https://localhost:8080/을 예로 들어보자.
    • 스키마 : https
    • HOST : localhost
    • Port : 8080

SOP (Same Origin Policy)

  • 동일한 Origin만 리소스를 공유할 수 있게 하는 정책
  • SOP를 준수하지 않으면 CORS 에러가 발생한다.
    • 단, origin이 다르더라도(SOP를 위반하더라도) CORS정책을 지키기만 한다면, 다른 origin의 리소스를 요청할 수 있다.

CORS, SOP의 등장배경

여기까지 보았을 때는, CORS고 SOP고 그냥 다른 origin이더라도 자유롭게 리소스 요청을 하면 되지 않나?라고 생각 들 수도 있다.
하지만 클라이언트 어플리케이션은 사용자의 공격에 너무나도 취약하기 때문에(웹 어플리케이션은 소스코드가 개발자 도구같은곳에 노출되어 있으므로), 악의를 가진 사용자가
CSRF(Cross Site Request Forgery) 혹은 XSS(Cross Site Scripting)공격을 할 수 있기 때문에,
그에 대비하기 위함이다.

알고 갈 사항

Cross Origin, 즉 다른 출처인지 파악하는 로직이 서버에 구현된 것이 아닌, 웹 브라우저에만 구현되어 있는 스펙이라고 한다.
따라서 CORS를 위반하더라도, 서버는 Origin을 굳이 파악하지 않으므로 정상적으로 리소스를 반환하지만 웹 브라우저에서는 CORS를 위반의 결과로 응답이 나왔다고 판단할 수 있으므로, 정상적으로 나온 응답을 버려 버린다.

Access Control Allow Origin

  • 다른 Origin에서도 자원을 이용할 수 있는지 여부를 나타낸다.
  • 서버의 Response에 헤더로 담겨진다.
    • 클라이언트 단에서 HTTP 통신 헤더인 Origin 헤더에 요청을 보내는 곳의 정보를 담고 서버로 요청을 보낸다.
    • 이후 서버는 Access-Control-Allow-Origin 헤더에 다음과 같은 허용 유무 정보를 담아 보낸다
      • Access-Control-Allow-Origin: *(와일드 카드) → 전체 origin에 대해서 리소스 접근을 허용한다.
      • Access-Control-Allow-Origin: <origin> → 특정 origin(꺽새괄호 안의 url)에 대해서만 접근을 허용한다.
      • Access-Control-Allow-Origin: null → 접근을 허용하지 않는다.
    • 허용 시
      • 클라이언트는 헤더의 값과 비교해 정상 응답임을 확인하고 지정된 요청을 보낸다.
      • 서버는 요청을 수행하고 200OK 코드를 응답한다.
  • Access-Control-Allow-Origin은 여러 출처를 명시할 수 없다
    • 여러 출처에서 접근을 허용하려면, 따로 white list에 리소스 접근을 허용할 origin을 기입해 두고, 요청이 올 시 허용여부를 결정한다.

CORS 동작방식

프리플라이트 요청(Preflight Request)

출처 : http://wiki.gurubee.net/display/SWDEV/CORS+(Cross-Origin+Resource+Sharing)

본격적인 교차 출처 HTTP 요청 전(preflight)에 서버 측에서 그 요청의 메서드와 헤더에 대해 인식하고 있는지를 확인하는 CORS 요청. HTTP메소드는 GET, POST 등이 아닌 OPTIONS를 이용한다.

  1. 실제 요청을 보내도 안전한지 판단하기 위해 preflight request를 먼저 보낸다.
  2. preflight response에서 허용하는 조건이 확인되면, 실제 요청을 보낸다.
  3. 실제 응답을 받는다.

장점

  • 실제 요청을 보내기 전에 미리 권한 확인을 할 수 있기 때문에, 실제 요청을 처음부터 통째로 보내는 것보다 리소스 측면에서 효율적이다. (만약 허용되지 않은 origin이라면, 실제 요청에 드는 리소스를 절약하는 꼴이므로)
  • CORS에 대비가 되어있지 않은 서버를 보호할 수 있다. CORS 이전에 만들어진 서버들은 SOP 요청만 들어오는 상황을 고려하고 만들었기 때문에 CORS에 대한 대비가 되어있지 않다.

단점

  • preflight 요청으로 인해 트래픽이 증가할 수 있는데, 이는 서버의 헤더 설정으로 캐싱이 가능하다.

단순 요청(Simple Request)

GET, HEAD, POST메소드에 대해서만 가능한 요청이다.

요청 자체가 CSRF등으로부터 안전할 것이라고 가정한다.

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

다른 도메인 API서버에서 자신을 인증하여 정상적인 응답을 받기 위해 쿠키를 통한 인증을 한다.

클라이언트와 서버 각각에서 해야 하는 작업이 있는데,

클라이언트 : 요청을 credentials 모드로 설정한다.

서버 : 응답헤더를 Access-Control-Allow-Crendentials: true 로 설정한다

주의할 점

  1. Access-Control-Allow-Origin의 값에 scheme과 port는 꼭 명시되어야 한다 (origin 스펙을 만족하여야 한다.)
  2. Access-Control-Allow-Origin: *Access-Control-Allow-Credentials: true는 함께 사용할 수 없다.
    1. Access-Control-Allow-Credentials: true를 사용하는 경우는 사용자 인증이 필요한 리소스 접근이 필요한 경우인데, 만약 Access-Control-Allow-Origin: *를 허용한다면 CSRF 공격에 매우 취약해져 악의적인 사용자가 인증이 필요한 리소스를 마음대로 접근할 수 있기 때문에, 정책상으로 아예 동작하지 않도록 막아버린 것입니다.

      따라서 Access-Contorl-Allow-Credentials: true인 경우에는 반드시 Access-Control-Allow-Origin의 값이 하나의 origin 값으로 명시되어 있어야 정상적으로 동작합니다.

서버단에서 CORS 에러 해결법(CORS 정책 준수하는 법)

  1. 커스텀 필터 생성
  • Filter를 구현하는 구현체 필터를 하나 생성 후, doFilter 메소드에서 OPTIONS HTTP 메소드 요청 시 반환할 헤더들을 정의한다. (ex : Access-Control-Allow-Origin, Access-Control-Allow-Credentials 등)
  • 이때 Filter는 Component으로 등록해 주어야 한다.
  1. 컨트롤러 단에서 @CrossOrigin 어노테이션의 옵션으로 허용할 origin을 설정해 준다.
  • 이때 메서드 단 혹은 컨트롤러 클래스단 두가지에 달아줄 수 있다.
  1. Config 클래스에서 WebMvcConfigurer를 Bean으로 등록한 후, 허용할 origin을 추가한다.

ref

profile
우상향 하는 개발자

0개의 댓글