CORS

Yeonn·2023년 9월 2일
0

CS 공부

목록 보기
8/13
post-thumbnail

Origin? : Protocol + Host + Port
https://yeonn.com:80/posts/1234?data=567
프로토콜: https://, 도메인: yeonn.com, 포트번호: 80
-> Origin: https://yeonn.com:80

✔️ SOP / CORS ?

CORS에 대해 알아보기 전 SOP에 대해 먼저 알아야한다 !

SOP( Same Origin Policy ): 동일 출처 정책

SOP 는 다른 Origin 으로 요청을 보낼 수 없도록 금지하는 브라우저의 기본적인 보안 정책이다. 동일한 Origin으로만 요청을 보낼 수 있는 절대적인 규칙이다. 이는 보안을 위해 필요한 정책이다. 하지만 기술 발달로 인해 서로 다른 Origin끼리 데이터를 주고받아야 하는 일이 많아졌고, 별도 예외 사항 지정( CORS ) 함으로써 다른 Origin끼리도 자원을 공유할 수 있도록 한다.

CORS( Cross Origin Resource Sharing ): 교차 출처 자원 공유

CORS 는 다른 Origin으로 요청을 보내기 위해 지켜야 하는 정책이다. CORS Error 를 만나면 당황스럽고 짜증날 수 있지만 ! 사실은 SOP 로 인해 막히게 될 요청을 풀어주는 정책이다.

✅ CORS 기본적인 동작 원리
1. 클라이언트에서 HTTP 요청의 헤더에 Origin을 담아 전달
2. 서버는 응답 헤더에 Access-Control-Allow-Origin을 담아 클라이언트로 전달
3. 클라이언트에서 Origin과 서버가 보내준 Access-Control-Allow-Origin을 비교
-> 보냈던 요청의 Origin과 서버가 보내준 Access-Control-Allow-Origin이 유효하지 않을 시 그 응답을 사용하지 않고 버린다 ( CORS 에러 ! )

✔️ CORS 작동 방식 3가지 시나리오

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

Preflight( 예비요청 ) 을 통해 서버와 잘 통신되는지 확인 한 후 요청을 보낸다.

OPTIONS/path HTTP/1.1
Origin: http://localhose: 3000
Access-Control-Request-Methods: POST
Access-Control-Request-Headers:Content-Type

예비 요청의 HTTP 메소드는 GET이나 POST가 아닌 OPTIONS 라는 요청이 사용된다. 또 Origin 출처와 실제 요청에 사용할 메서드, 실제 요청에 사용할 헤더를 설정해서 보낸다.

서버는 이 요청에 대한 응답으로 허용과 금지에 대한 헤더 정보를 담아 브라우저로 보낸다.

Http/1.1 200 OK
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: Content-Type
Access-Control-Max-Age: 86400 
// 해당 예비 요청이 브라우저에 캐시 될 수 있는 시간 초 단위로 설정

브라우저는 보낸 요청과 서버가 응답해 준 정책을 비교 후 해당 요청이 안전한지 확인하고 본 요청을 보낸다. 서버가 본 요청에 대한 응답을 하면 최종적으로 이 응답 데이터를 자바스크립트로 넘겨준다. 이는 보안 강화에 도움이 된다. 하지만 실제 요청에 걸리는 시간이 늘어나게 되어 어플리케이션 성능에 영향을 미치는 크나큰 단점이 발생할 수 있다. Access-Control-Max-Age 헤더에 캐시될 시간 명시를 통해 캐싱 최적화를 할 수 있다.

2. Simple Request ( 단순 요청 )

Preflight( 예비요청 )을 생략하고 바로 서버에 직행으로 본 요청을 보낸다. 그리고 서버가 보내준 Access-Control-Allow-Origin 헤더를 브라우저가 CORS 정책 위반에 대한 여부를 검사한다. 그리고 아래의 모든 조건이 충족 되어야 가능하다.

Simple Request 조건 !

  • Method: GET, HEAD, POST
  • Header: Accept ,Accept-Language, Content-Language, Content-Type, DPR, Downlink, Save-Data, Viewport-Width, Width
  • Content-Type 헤더: application/x-www-form-urlencoded, multipart/form-data, text/plain 중 하나여야만 한다! ( 아닐 경우 예비 요청으로 동작 )

하지만 대부분 HTTP API 요청은 text/xml, application/json 으로 통신하기 때문에 Content-Type이 위반되기 때문에 해당하기 힘들다.

3. Credentialed Request ( 인증된 요청 )

클라이언트에서 서버에게 자격 인증 정보( Credential )를 실어 요청할 때 사용되는 요청이다. 자격 인증 정보란 세션 ID가 저장되어 있는 쿠키 ( Cookie ) 혹은 Authorization 헤더에 설정하는 토큰 값이다. 이 때, 클라이언트도, 서버도 정해진 조건이 모두 충족되어야 가능하다.

✔️ 클라이언트에서 인증 정보를 보내도록 설정하기 !
브라우저가 제공하는 요청 API들은 별도의 옵션 없이 브라우저의 쿠키와 같은 인증과 관련된 데이터를 함부로 요청 데이터에 담지 않도록 되어 있다. 이 때 요청에 인증과 관련된 정보를 담을 수 있게 해주는 옵션이 바로 credentials 옵션이다.

credential

  • same-origin( 기본값 ): 같은 출처 간 요청에만 인증 정보를 담을 수 있다.
  • include: 모든 요청에 인증 정보를 담을 수 있다.
  • omit: 모든 요청에 인증 정보를 담지 않는다.

✔️ 서버에서 인증된 요청에 대한 헤더 설정하기 !
서버에서도 일반적인 CORS 요청과는 다르게 대응해야 한다.

서버의 대응 !

  • 응답 헤더의 Access-Control-Allow-Credentials 항목 true 설정
  • 응답 헤더의 Access-Control-Allow-Origin 값에 와일드카드 문자 ( * ) 사용 불가
  • 응답 헤더의 Access-Control-Allow-Methods 값에 와일드카드 문자 ( * ) 사용 불가
  • 응답 헤더의 Access-Control-Allow-Headers 값에 와일드카드 문자 ( * ) 사용 불가

✔️ CORS 해결 방법

1. Chrome 확장프로그램

Chrome에서는 CORS 문제를 해결하기 위한 확장 프로그램을 제공한다. 'Allow CORS: Access-Control-Allow-Origin' 크롬 확장 프로그램을 설치해서 사용할 수 있고 로컬( localhost ) 환경에서 API를 테스트 시, CORS 문제를 해결할 수 있다.

https://chrome.google.com/webstore/detail/allow-cors-access-control/lhobafahddgcelffkeicbaginigeejlf

2. 프록시 사이트 이용

모든 출처를 허용하는 '서버 대리점' 을 통해 요청한다. 서버 대여 서비스들은 모두 악용 사례 때문에 API 요청 횟수 제한을 두어 실전에서 사용하기는 무리이다 !

✅ heroku 프록시 서버
http://cors-anywhere.herokuapp.com/corsdemo
✅ CORS proxy app 프록시 서버( axios 라이브러리 설치 필요 )
https://github.com/raravel/cors-proxy
✅ CORS.SH 프록시 서버
https://github.com/raravel/cors-proxy

3. Access-Control-Allow-Origin 세팅

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

4. Webpack Dev Server로 리버스 프록싱

module.exports = {
  devServer: {
    proxy: {
      '/api': {
        target: 'https://api.evan.com',
        changeOrigin: true,
        pathRewrite: { '^/api': '' },
      },
    }
  }
}

5. chrome 브라우저 설정 변경

// 터미널- open
open -na Google\ Chrome --args --user-data-dir=/tmp/temporary-chrome-profile-dir --disable-web-security
// 터미널 -close
open -a "Google Chrome" --args --profile-directory=Default --enable-web-security

0개의 댓글