[Web] CORS

j-yong98·2026년 3월 2일

Web

목록 보기
1/3

SOP

Web에서는 보안상의 이유로 출처(Origin)가 다른 곳에 보내는 요청은 기본적으로 제한된다.

이 정책을 Same-Origin Policy(SOP) 라고 한다.

여기서 말하는 Origin은 다음 세 가지 요소를 말한다.

  • Protocol
  • Host
  • Port

예를 들면, https://example.comhttps://api.example.com 는 Host가 다르기 때문에 서로 다른 Origin이다.

SOP의 제한 범위

SOP가 모든 교차 출처의 요청을 막는 것은 아니다. 이미지, CSS, 스크립트, 동영상과 같은 리소스는 다른 출처에도 정상적으로 로드되는 것을 확인할 수 있다.

브라우저는 단순히 화면에 표시하거나 실행하는 리소스는 허용한다. 하지만, Javascript 코드가 응답 내용을 직접 읽는 것을 제한한다.

fetch("http://localhost:4000/data")
        .then((r) => r.json())

요청을 보내면 실제로 서버에 전달되고, 서버에서도 응답을 보내준다. 하지만 브라우저가 그 응답을 막는다. 이것이 CORS 에러의 본질이다.

왜 제한할까?

만약 이런 제한이 없다면, 사용자가 로그인한 상태에서 악성 사이트가 사용자 정보를 요청을 보낼 수 있다. 이런 경우 SOP가 없다면 사용자의 정보가 그대로 노출될 수 있다.

그래서, SOP는 사용자의 인증정보가 자동으로 포함되는 요청으로부터 데이터를 보호하기 위한 정책이다.

CORS

SOP로 인해 교차 출처에 대한 자원 공유 정책이 필요해졌다. 이것이 바로 CORS(Cross-Origin Resource Sharing) 이다.

CORS는 교차 출처 요청을 무조건 허용하는 정책이 아니라, 서버가 특정 출처에 대해 “이 응답을 읽어도 된다”고 명시적으로 선언하는 메커니즘이다.

즉, 브라우저가 임의로 허용하는 것이 아니라 서버의 허용 의사에 따라 브라우저가 응답을 열어주는 구조다.

CORS의 동작 방식

브라우저가 다른 출처로 요청을 보내면 서버는 응답 헤더에 다음과 같은 값을 포함할 수 있다.

Access-Control-Allow-Origin: http://localhost:4000

이 헤더가 존재하고 요청을 보낸 Origin과 일치하면 브라우저는 해당 응답을 사용할 수 있도록 허용한다.

CORS는 서버가 공격을 막는 기능이 아니다. curl 이나 postman 을 사용해 보면 CORS가 발생하지 않는다. CORS는 브라우저가 적용하는 보안 정책이기 때문이다.

CORS 응답 헤더 종류

Access-Control-Allow-Origin: http://localhost:3000

어떤 Origin에 대해서 응답을 허용할 것인지 명시하는 헤더이다. 요청의 Origin 과 비교해서 일치하는 경우 허용한다.

Access-Control-Allow-Method: GET, POST, PUT, DELETE

허용할 HTTP Method 목록을 의미한다.

Access-Control-Allow-Headers: Authorization, Content-Type

클라이언트가 요청할 수 있는 헤더 목록을 의미한다.

Access-Control-Allow-Credentials: true

쿠키, 세션과 같은 인증 정보 허용 여부를 의미한다. 해당 옵션을 true로 설정할 경우 Access-Control-Allow-Origin 헤더의 경우 * 를 혀용하지 않는다.

Access-Control-Max-Age: 3600

Prefilght 요청 결과를 브라우저가 캐싱하는 시간을 의미한다.

CORS 요청 방식

Simple Request

특정 조건을 만족하면 Preflight 요청을 보내지 않고 바로 요청을 보낸다. 특정 조건은 다음과 같다.

  1. HTTP Method: GET, POST, HEAD
  2. 허용된 Request Header: Accept, Accept-Language, Content-Language, Content-Type 등
  3. Content-Type 제한: application/x-www-form-unlencoded, multipart/form-data, text/plain

위 조건을 모두 만족하기는 힘들다. 따라서 대부분의 요청은 Preflight 요청이 발생한다고 생각하자.

Preflight Request

Preflight 요청은 실제 요청을 보내기 전에 브라우저가 OPTIONS 메서드를 사용해 서버에 먼저 보내는 사전 확인 요청이다.

브라우저는 요청이 조건을 만족하지 않는 경우, 곧바로 실제 요청을 보내지 않고 먼저 서버에 이 요청을 보내도 되는지 확인한다. Preflight는 다음과 같은 과정을 통해 이루어진다.

  1. 브라우저가 OPTIONS 메서드로 사전 요청 전송

    OPTIONS /data
    Origin: http://localhost:3000
    Access-Control-Request-Method: POST
    Access-Control-Request-Headers: Authorization, Content-Type

    브라우저는 실제 보낼 요청의 정보를 포함 시켜 전송한다.

  2. 서버가 허용 정책을 응답 헤더에 포함하여 응답

  3. 브라우저가 허용 여부 검증

    사전에 보낸 요청과 허용 정책을 비교한다. Origin이 허용 목록에 포함되는지, 허용된 Method인지, 요청 헤더가 Allow-Headers에 포함되는지 등을 비교한다.

  4. 검증 통과 시 실제 요청 전송

이런 사전 요청을 매번 보내서 확인하는 것은 서버의 많은 요청을 보내기 때문에 비용적인 측면에서 비효율적이다. 그래서 Access-Control-Max-Age 를 통해서 사전 요청의 응답을 캐싱할 수 있다.

0개의 댓글