[CS] CORS

hyeom1027·2022년 7월 5일
0

CS

목록 보기
1/1
post-thumbnail

정의

교차 출처 리소스 공유(Cross-Origin Resource Sharing, CORS)는 추가 HTTP 헤더를 사용하여, 한 출처에서 실행 중인 웹 애플리케이션이 다른 출처의 선택한 자원에 접근할 수 있는 권한을 부여하도록 브라우저에 알려주는 체제이다. - MDN

브라우저에서 다른 출처의 리소스를 공유하는 방법

URL 구조

출처(Origin) 이란?

Protocol + host + port

동일 출처 (Same Origin)

origin이 모두 같은 것

SOP (Same-Origin Policy)

같은 출처에서만 리소스를 공유할 수 있다는 규칙

브라우저에서 다른 서버에서 요청할 경우에 해당되고, 브라우저를 거치지 않고 서버 간 통신을 할 경우에는 이 정책이 적용되지 않음

🔒 통신에 아무런 제약이 없다면 악의적인 사용자가 소스코드를 보고 **CSRF, XSS**와 같은 방법을 이용하여 **정보를 탈취**할 수 있음

동작 방식

단순 요청 (simple Request)

서버에게 바로 요청을 보내는 방법

  1. 서버에 API를 요청
  2. 서버는 Access-Control-Allow-Origin 헤더를 포함한 응답을 브라우저에 보냄
  3. 브라우저는 Access-Control-Allow-Origin 헤더를 확인해서 CORS 동작을 수행할지 판단

조건

  • 요청 메서드(method) ⇒ GET, HEAD, POST
  • Accept, Accept-Language, Content-Language, Content-Type, DPR, Downlink, Save-Data, Viewport-Width, Width를 제외한 헤더를 사용불가 ⇒ 사용자 인증 헤더 Authorization 불가능
  • Content-Type 헤더는 application/x-www-form-urlencoded, multipart/form-data, text/plain 중 하나를 사용 ⇒ 대부분의 rest api는 application/json 를 사용함

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

서버에 예비 요청을 보내서 안전한지 판단 후 본 요청을 보내는 방법

  1. 실제 요청 전 OPTIONS 메서드를 통해 예비 요청을 보냄
  2. 서버는 예비 요청에 대한 응답으로 Access-Control-Allow-Origin 헤더를 포함한 응답을 브라우저에 보냄
  3. 브라우저는 단순 요청과 동일하게 Access-Control-Allow-Origin 헤더를 확인해서 CORS 동작을 수행할지 판단
  4. 올바른 경우 본 요청 보냄

CORS 해결방법

Access-Control-Allow-Origin 응답 헤더 세팅

서버측 응답에서 접근 권한을 주는 헤더를 추가하여 해결

// server source
app.use((req, res, next) => {
  res.header("Access-Control-Allow-Origin", "*"); // 모든 도메인
  res.header("Access-Control-Allow-Origin", "https://example.com"); // 특정 도메인
});

cors 모듈 사용

const cors = require("cors");
const app = express();

app.use(cors());

아무 옵션없이 설정하면 모든 cross-origin 요청에 대해 응답이므로,

특정 도메인이나 특정 요청에만 응답하게 옵션을 설정하는 것이 좋음

  • 특정 도메인 접근 허용
const options = {
  origin: "http://example.com", // 접근 권한을 부여하는 도메인
  credentials: true, // 응답 헤더에 Access-Control-Allow-Credentials 추가
  optionsSuccessStatus: 200, // 응답 상태 200으로 설정
};

app.use(cors(options));
  • 특정 요청에 대한 접근 허용
app.get("/example/:id", cors(), function (req, res, next) {
  res.json({ msg: "example" });
});

webpack-dev-server proxy 기능

  • 리액트 개발환경에서, 서버쪽 코드를 수정하지 않고 해결할 수도 있다. 아래와 같이 프록시 속성을 설정하면, 서버에서 해당 요청을 받아준다.
// 프록시 쓰지 않았을때
// localhost:8080(클라이언트 측) --X (CORS)--> domain.com (서버 측)

// 프록시를 설정 후
// localhost:8080(클라이언트 측) --O 프록시가 설정된 Webpack Dev Server--> domain.com (서버 측)

module.exports = {
  devServer: {
    proxy: {
      "/api": {
        target: "domain.com",
        changeOrigin: true,
      },
    },
  },
};

중간의 프록시 서버 덕분에, domain.com 서버에서는 같은 도메인(domain.com)에서 온 요청으로 인식하여 CORS 에러가 발생하지 않는다.

package.json에 proxy값을 설정

create-react-app 으로 생성한 프로젝트에서는, package.json 에 proxy 값을 설정하여 proxy 기능을 활성화 하는 방법도 있다.

{
    //...
    "proxy": "http://localhost:4000"
}

JSONP

Json with padding으로 각기 다른 도메인에 상주하는 서버로부터 데이터를 요청하기 위해 사용

Json은 경량의 Data 교환 형식인데 다른 도메인에서 Json 데이터를 가져올 때 Javascript에서 SOP 정책으로 인해 데이터를 못 가져오는 경우가 발생

⇒ Json Data를 callback 함수에 넣어서 전송하는 Jsonp을 사용

코드 예시

<!-- Frontend -->
<!DOCTYPE html>
<html>
  <script>
    function fn (data) {
      console.log(data) // data
    }
  </script>
  <script
    type="application/javascript"
    src="http://localhost:3001/cors?callback=fn"
  >
  </script>
</html>-
// Backend
router.get('/cors', (req, res, next) => {
  res.send(`${req.query.callback}('data')`)
})

jsonp가 가능한 이유

웹 브라우저에서 실행되는 Javascript는 동일 출처 정책에 따라 XMLHttpRequest 등의 직접적인 Http 통신을 이용해 데이터를 가져오는 것은 불가능

그러나 HTML

Jsonp은

보안상 이슈 ⇒ 보통은 개발할 때 사용


0개의 댓글