CORS error가 발생하는 이유 그리고 왜 필요할까?

Benza·2022년 4월 13일
0

🔎

목록 보기
4/11

개요

보안이슈

CORS를 알아보기 전에

SOP(Same Origin Policy)

다른 출처의 리소스를 사용하는 것에 의한 공격을 받을 경로를 제한하는 보안 방식

여기서 다른 출처(Origin)란 무엇일까? 출처의 정의를 먼저 알아보자

출처(origin) : URL의 스킴(Protocal, Host, Port)을 통해 정의된다. 서로 다른 두 객체의 스킴이 일치 할 때 같은 출처라고 볼 수 있다.

*IE의 경우는 port가 다르더라도 같은 출처로 인식

http://localhost 와 동일한 출처는 무엇일까요?
1. https://localhost
2. http://localhost:80
3. http://127.0.0.1
4. http://localhost/api/cors

정답 : 2,4

1번은 프로토콜이 다르다.
3번은 http://127.0.0.1가 localhost는 맞으나 브라우져는 string value로 비교하기 때문에 다른 출처로 인식한다

어떤 경우 SOP가 사용되는가

sop 예제

다른 출처의 리소스가 필요한 경우에 CORS가 필요하다.


CORS(Cross-Origin Resource Sharing)

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

어떤 경우 CORS가 필요할까?

다음과 같은 경우에 사이트간 HTTP 요청을 허용한다.

  • XMLHttpRequest와 Fetch API 호출
  • 웹 폰트(CSS 내 @font-face에서 교차 도메인 폰트 사용 시)
  • WebGL 텍스쳐
  • drawImage() (en-US)를 사용해 캔버스에 그린 이미지/비디오 프레임
  • 이미지로부터 추출하는 CSS Shapes

동작 방식

웹 브라우저에서 해당 정보를 읽는 것이 허용된 출처를 서버에서 설명할 수 있는 새로운 HTTP 헤더를 추가함으로써 동작

CORS 접근제어 시나리오 3가지

  • Simple Request
  • Preflight Request
  • Credentialed Request

Preflight Request

사전 확인 작업, 되는지 안되는지 먼저 물어보는 작업

Request Method: OPTIONS를 통해 다른 도메인의 리소스에 요청이 가능한 지 확인 작업
요청이 가능하다면 실제 요청(Atual Request)를 보낸다.

prefligt request formet

위의 응답/요청 시 주고 받는 데이터 포멧

요청

  • origin: 출처
  • Access-Control-Request-Method: 실제 요청의 메서드
  • Access-Control-Request-Headers: 실제 요청의 추가 헤더

응답

  • Access-Control-Allow-Origin: 서버 측 허가 출처
  • Access-Control-Allow-Methods: 서버 측 허가 메서드
  • Access-Control-Allow-Headers: 서버 측 허가 헤더
  • Access-Control-Max-Age: Preflight 응답 캐시 기간(똑같은 요청 리소스를 줄이기 위해 캐시 사용)

Simple Request

Preflight Request 없이 바로 Actual Request를 보내는 것으로 즉시 CORS를 확인 하는 절차이다.
특정 조건을 만족함으로 Simple Request 방식을 사용할 수 있다.

*Preflight Request 스킵하는 방법 Simple Request 사용하기

-GET, POST, HEAD 메서드를 사용하고 Content-Type이 application/x-www-form-urlencoded, multipart/form-data, text//plain이어야한다.

간편하게 Simple Request를 쓰면 되지 왜 Preflight가 필요할까?

CORS spec이 생기기 이전에 만들어진 서버들은 브라우저의 SOP(same origin policy) request만 가능하다는 가정하에 만들어졌는데, cross-site request가 CORS로 인해서 가능해졌기 떄문에 이런 서버들은 cross-site request에 대한 security mechanism이 없다보니 보안적으로 문제가 생길수 있으니 이런 서버들을 보호하기 위해 CORS spec에 preflight request를 포함하게 되었다.

또한, CORS를 모르는 서버를 위해서 필요하다.

CORS 처리가 되어 있지 않은 경우 Preflight가 없으면 어떤 client에서 요청이라도 서버가 정상적으로 수행하고 응답한다.
그러나 브라우져에서 CORS 에러를 이르킨다.
이미 서버에서 정상적으로 수행을 했기 떄문에 DELETE 요청이 들어왔을 경우 서버는 디비를 지워버릴것이다;;;
그러나 Preflight Request를 보낸다면 서버는 Actual Request를 받지 않아 수행하지 않을 것이다.

Credentialed Request

인증 관련 헤더를 포함할 떄 사용하는 요청이다.
client 측에서 credentials: include를 담아 전송하는 방식으로 확용된다.


CORS error 해결하기

크게 3가지 방법으로 나눌 수 있다.

  1. 직접 header에 Access-Control-Allow-Origin 설정
  2. JSONP(JSON with Padding)
  3. front proxy server 설정

Access-Control-Allow-Origin 설정

CORS error가 발생한다면 서버 측에서 Access-Control-Allow-Origin 헤더에 접근권한을 설정할 수 있다.
header('Access-Control-Allow-Origin', '*')를 설정하게 된다면 모든 외부 출처에서 접속을 할 수 있게 된다. 하지만 보안적인 이슈에 직면할 수 있기 때문에 이러한 방법은 지양해야겠습니다.
그렇기에 외부에서 사용한다는 요청에 대한 심사를 거친 후 Access-Control-Allow-Origin에 외부 출처를 일일이 적용하는것이 좋습니다. 외부 출처를 추가하는 방법은 origin이 "https://test.com"일 경우 "Access-Control-Allow-Origin: https://testA.com'으로 헤더를 적용하여 주면 됩니다.

var app = require('express')()
var cors = require('cors')

// Access-Control-Allow-Origin 적용방법1: 직접 헤더에 적용
app.all('/*', function (req, res, next) {
  res.header('Access-Control-Allow-Origin', '*')
  res.header('Access-Control-Allow-Headers', 'X-Requested-With')
  next()
})

// Access-Control-Allow-Origin 적용방법2: cors 미들웨어 사용
app.use(cors())

app.listen(80, function () {
  console.log('http server is listening on port 80')
})

cors()를 적용하게 된다면 모든 origin을 허용하게 된다. 일부의 origin을 적용하고 싶다면 cors 미들웨어 문서를 참고하시길...

JSONP 설정

다른 도메인에 접근할 수 있는 우회적인 기법 중에 JSONP(JSON with Padding) 방법이 있다. 웹 브라우저에서 실행되는 Javascript는 통신을 이용해 외부 출처에서 데이터를 받아오는 것이 불가능하지만, HTML

문법 오류가 발생하는 이유는 Javascript에서 변수, 상수로 정의 되지 않고 json형식이 입력되면서 발생한다.

JSONP는 이러한 웹브라우저 특성을 이용하여 json 데이터를 서버가 콜백함수로 감싸 javascript 문법을 유효하게 만들어 전달하여 클라이언트에서 응답을 받을 수 있게 됩니다.

"http://server.example.com/User/1234"요청을 예로 든다면

parseResponse 콜백함수를 JSONP 요청을하기위해 포함을 한다.

<script type="application/javascript"
        src=http://server.example.com/Users/1234?callback=parseResponse">
</script>

외부의 서비스인 server.example.com은 JSON 데이터를 패딩하여 클라이언트에 전송합니다.

parseResponse({"Name": "Foo", "Id": 1234, "Rank": 7})

JSONP 내용을 확인해보시면 아시겠지만 결국 서버에서 패딩을 요청한 콜백함수로 감싸줘 전송을 해야한다는 문제가 발생한다. 외부 API에서 콜백함수를 제공하지 않는다면 사용할 수 없는 방법이다.

Proxy 설정

서버에서 요청을 하게될 때에는 브라우저의 규약인 CORS 정책에 영향을 받지 않습니다. 그러한 이점을 이용하여 Proxy Server라는 출처를 통하기 때문에 CORS 정책을 위반하지 않게되어 우회 할 수 있게됩니다.


참고 자료

(MDN)교차 출처 리소스 공유 (CORS)
(YouTube)우아한Tech-[10분 테코톡]🌳 나봄의 CORS
(velog)wiostz98kr-CORS 개념 정리(preflight, simple, credentialed request)+SOP
Fetch Living Standard
(티스토리)devport-[개발] Frontend에서 Cors 해결방법

profile
Understanding the impression

0개의 댓글