Cross-Origin Resource Sharing (CORS) - HTTP | MDN

Cross-Origin Resource Sharing (CORS) is an HTTP-header based mechanism that allows a server to indicate any origins (domain, scheme, or port) other than its own from which a browser should permit loading resources. CORS also relies on a mechanism by which

developer.mozilla.org

위 출처 내용을 정리한 글입니다.

 

보안 상의 이유로, 브라우저는 스크립트에서 시작한 교차 출처 HTTP 요청을 제한합니다.

다른 출처의 리소스를 불러오려면 그 출처(다른 출처)에서 올바른 CORS 헤더를 포함한 응답을 반환해야 합니다.

 

 

교차 출처 리소스 공유(Cross-Origin Resource Sharing, CORS)는

추가 HTTP 헤더를 사용하여,

한 출처에서 실행 중인 웹 애플리케이션이

다른 출처의 선택한 자원에 접근할 수 있는 권한을 부여하도록

브라우저에 알려주는 체제입니다.

 

웹 애플리케이션(브라우저)은 리소스가

자신의 출처(도메인, 프로토콜, 포트)와 다를 때 교차 출처 HTTP 요청을 실행합니다.

 

출처(Origin)

웹 콘텐츠의 출처(origin)는 접근할 때 사용하는 URL의 스킴(프로토콜), 호스트(도메인), 포트로 정의됩니다.

두 객체의 스킴, 호스트, 포트가 모두 일치하는 경우 같은 출처를 가졌다고 말합니다.

 

 

 

 

브라우저가 프론트엔드 JavaScript 코드 대해

교차 출처(cross-origin)에 대한 응답에 접근하는 것을

차단하는지 여부를 결정하는 HTTP headers 전송으로 이루어진 시스템입니다.

 

 

기능적 개요

교차 출처 리소스 공유 표준은 웹 브라우저에서 해당 정보를 읽는 것이 허용된 출처를 서버에서 설명할 수 있는 새로운 HTTP 헤더를 추가함으로써 동작합니다.

추가적으로, 서버 데이터에 부수 효과(side effect)를 일으킬 수 있는

HTTP 요청 메서드(GET을 제외한 HTTP 메서드)에 대해,

CORS 명세는 브라우저가 요청을 OPTIONS 메서드로 "프리플라이트"(preflight, 사전 전달)하여 지원하는 메서드를 요청하고,

서버의 "허가"가 떨어지면 실제 요청을 보내도록 요구하고 있습니다.

또한 서버는 클라이언트에게 요청에 "인증정보"(쿠키, HTTP 인증)를 함께 보내야 한다고 알려줄 수도 있습니다.

 

CORS 실패는 오류의 원인이지만, 보안상의 이유로 JavaScript에서는 오류의 상세 정보에 접근할 수 없으며, 알 수 있는 것은 오류가 발생했다는 사실 뿐입니다. 정확히 어떤 것이 실패했는지 알아내려면 브라우저의 콘솔을 봐야 합니다.

 

 

 

사전요청이 일어나는 조건

단순 요청(Simple requests)이면 하지 않는다

단순 요청 조건

  • 다음 중 하나의 메서드
  • 유저 에이전트가 자동으로 설정 한 헤더 (예를들어, Connection, User-Agent (en-US), Fetch 명세에서 "forbidden header name"으로 정의한 헤더)외에, 수동으로 설정할 수 있는 헤더는 오직 Fetch 명세에서 "CORS-safelisted request-header"로 정의한 헤더 뿐입니다.
  • 요청에 사용된 XMLHttpRequestUpload (en-US) 객체에는 이벤트 리스너가 등록되어 있지 않습니다. 이들은 XMLHttpRequest.upload 프로퍼티를 사용하여 접근합니다..
  • 요청에 ReadableStream 객체가 사용되지 않습니다.

 

 

시나리오

간단한 시나리오(단순 요청)

https://foo.example 의 웹 컨텐츠가 https://bar.other 도메인의 컨텐츠를 호출

코드

아래는 간단 요청이다.

 

송수신

브라우저 => 서버

서버 => 브라우저

 

가장 간단한 시나리오는 Origin헤더(브라우저 요청) Access-Control-Allow-Origin 헤더(서버 응답) 사용하는

 

일반 시나리오(프리플라이트 요청)

"preflighted" request는 위에서 논의한 "simple requests" 와는 달리,

먼저 OPTIONS 메서드를 통해 다른 도메인의 리소스로 HTTP 요청을 보내 실제 요청이 전송하기에 안전한지 확인합니다.

 

cross-origin 요청은 유저 데이터에 영향을 줄 수 있기 때문에 이와같이 미리 전송(preflighted)합니다.

 

예시

위와 같은 이유로 preflighted 요청이 발생한다.

 

전체 흐름

 

클라이언트와 서버간의 완전한 통신을 살펴보겠습니다.

첫 번째 통신은 preflight request/response입니다.

 

preflight request가 완료되면 실제 요청을 전송합니다.

 

 

자격 증명이 포함된 요청(Requests with credentials)

기본적으로 교차 출처 XMLHttpRequest 또는 Fetch 호출에서 브라우저는 자격 증명을 보내지 않습니다.

XMLHttpRequest 또는 Requset 생성자가 호출될 때 특정 플래그를 설정해야 합니다.

 

예시

https://foo.example 에서 로드된 콘텐츠 속, 

https://bar.other 대한 Simple Request를 만들고 있음

기본적으로 호출은 쿠키 없이 이루어집니다.

이는 단순한 GET 요청이므로 preflighted 요청이 되지 않지만

브라우저는 Access-Control-Allow-Credentials: true 헤더가 없는 응답을 거부하고 호출하는 웹 콘텐츠에 응답을 제공하지 않습니다.

 

 

 

CORS 실행 사전 요청에서는 자격 증명이 포함되선 안됩니다.

Simple Request 이외 요청

사전 요청에 대한 응답은 Access-Control-Allow-Credentials: true를 지정하여 실제 요청이 자격 증명으로 이루어질 수 있음을 나타내야 합니다.

 

Credentialed requests에서 와일드카드

Access-Control-Allow-Origin

허용할 Origin 헤더

Access-Control-Allow-Headers

 

Access-Control-Allow-Methods

Access-Control-Expose-Headers

서버는 헤더를 반환할 '*' 와일드 카드를 반환해서는 안됩니다.

브라우저는 응답 헤더에 와일드 카드가 하나라도 있으면, Response 대한 접근을 거부하고, 콘솔에 에러를 출력합니다.

 

헤더에 와일드 카드가 포함되지 않고, 자격 증명(보통 쿠키) 있으면, 브라우저는 Response 접근을 허용합니다.

 

쿠키 정책

CORS 응답에 설정된 쿠키에는 타사 쿠키 정책이 적용됩니다.

 

이유

예제에서 CORS 요청 페이지는 foo.example 에서 유래됐지만,

CORS 응답에 설정된 쿠키는 bar.other 서버에 의해 전송됐기 때문입니다.

 

브라우저가 모든 타사 쿠키를 거부하도록 구성된 경우 저장되지 않습니다.

경우, 자격 증명 수행을 못하게 됩니다.

 

쿠키 정책 관련 헤더 SameSite

 

CORS 사양에 정의된 액세스 제어 헤더

Response

Access-Control-Allow-Origin

허용할 origin

Access-Control-Expose-Headers

브라우저가 액세스할 수 있는 허용 목록에 헤더

Access-Control-Max-Age

preflight request 결과를 캐시할 수 있는 기간

Access-Control-Allow-Credentials

단순 요청인 경우, 값이 true여야만 브라우저가 응답에 접근합니다

이외 사전 요청의 경우, 값이 true이면 자격증명요청을 허용한다는 뜻입니다.

Access-Control-Allow-Methods

허용할 HTTP 메서드(, POST, GET, DELETE…)

Access-Control-Allow-Headers

Preflight Request 실제 Request 사용할 있는 헤더

 

Request

Origin

요청이 시작된 서버 URL

경로 정보는 포함되지 않고 서버 이름만 포함

Access-Control-Request-Method

실제 요청이 이루어질 때 어떤 HTTP 메서드가 사용될지 서버에 알리기 위해 Preflight Request 사용

Access-Control-Request-Headers

실제 요청이 이루어질 때 어떤 HTTP 헤더가 사용될 것인지 서버에 알리기 위해 Preflight Request 사용

이 브라우저 측 헤더는 서버 측  Access-Control-Allow-Headers 헤더에 의해 응답됩니다.

 

HTTP 상태코드

  • 1xx (Informational): 요청이 수신되어 처리중
  • 2xx (Successful): 요청 정상 처리
  • 3xx (Redirection): 요청을 완료하려면 추가 행동이 필요
  • 4xx (Client Error): 클라이언트 오류, 잘못된 문법등으로 서버가 요청을 수행할 수 없음
  • 5xx (Server Error): 서버 오류, 서버가 정상 요청을 처리하지 못함

큰 분류에 대한 개념만 잡고 상세 코드는 그때그때 찾아보면 된다.

 

 

3xx (Redirection)

웹브라우저는 300번대 응답 헤더에 Location이 있으면 해당하는 값 주소로 자동이동한다.(리다이렉트)

리다이렉트 관련 알아둘 개념, PRG 패턴, 캐시

 

4xx (Client Error)

클라이언트 오류로 크게 3가지 관점에서 보면된다.

  • 문법 오류(부정확한 주소)
  • 인증되지 않은 접근(로그인)
  • 권한이 불충분한 접근(일반 사용자인데 관리자 페이지 접근)

5xx (Server Error)

400번대 오류랑 헛갈리면 안됨. 500번대 오류는 진짜 서버 문제인 경우에만 사용

DDNS 공격을 받아 응답을 못할 경우 500번대 에러가 될 수 있음

다른 예시로 400번대 오류랑은 다르게 똑같은 시도를 여러 번 했을 시 성공할 수도 있으면 500번대 에러는 아님

 

 

 

 

HTTP 헤더

HTTP 전송에 필요한 모든 부가정보를 담는다.

실질적인 정보는 바디

 

표준헤더가 엄청나게 많다.

추가로 커스텀 헤더를 정의해 사용할 수 있다.(클라이언트 단과 서버 단에서 처리할 수 있게 설정한다면)

 

Content-Type

HTTP 바디가 무엇인지 설명해주는 헤더

JSON인지, XML인지, text인지 등등, 미디어 타입이 무엇인지 

 

 

Referer

이전 웹 페이지 주소, 

이 헤더 정보를 토대로 유입 경로를 분석한다.

 

User-Agent

유저 브라우저 정보라 생각해도 된다.

이 값으로 장애가 발생한 브라우저가 무엇인지 특정할 수 있다.

 

HOST

이 헤더값 덕분에 (외부에 노출된) 하나의 IP를 공유하는 여러 서버를 구분할 수 있다.

 

Location

리다이렉션 시 이동할 주소를 알려준다.

 

 

 

 

쿠키

HTTP는 기본적으로 무상태 프로토콜이다.

쿠키는 이런 상태정보를 유지할 때 사용된다.

쿠키 사용 예시, 쇼핑몰에서 로그인 하지 않은 사용자가 장바구니에 물건을 담을때 그 정보를 유지할 때 쿠키를 사용한다.

 

쿠키는 초 단위로 유효시간을 정해 사용한다. 

쿠키를 삭제하는 기능은 따로 없다. 삭제하고 싶다면 서버에서 0초 쿠키를 발행하면 된다.

쿠키는 도메인 구조를 지원한다. 이 정보로 어느 경로에 이 쿠키가 유효한지 세밀하게 지정할 수 있다.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

HTTP 특징

비 연결성(connectionless)

무상태 프로토콜

  • 서버가 클라이언트의 상태를 보존하지 않는다.
  • 장점: 서버 확장성 높음(스케일 아웃)
  • 단점: 클라이언트가 추가 데이터 전송

클라이언트 서버 구조(Request, Response)

거의 모든 형태의 데이터 전송 가능

 

 

HTTP 메시지 구조

https://developer.mozilla.org/ko/docs/Web/HTTP/Messages

start line, header, body 로 이뤄진다.

 

start line

요청과 응답은 살짝 다르다.

 

요청(request-line)은 HTTP 메서드와 HTTP버전이 나온다.

응답(status-line)은 HTTP버전과 HTTP 상태코드가 온다.

 

 

 

HTTP 헤더

HTTP 전송에 필요한 모든 부가정보가 들어간다.

표준헤더를 주로 사용하나 커스텀 헤더도 만들어 사용할 수 있다. 

 

 

HTTP 메시지 바디

html 문서일 수도, 이미지일 수도, JSON일 수도 사실상 거의 모든 데이터를 전송할 수 있다.

 

 

 

 

HTTP API 설계 예시

안좋은 예시

  • 회원 목록 조회 /read-member-list
  • 회원 조회 /read-member-by-id
  • 회원 등록 /create-member
  • 회원 수정 /update-member
  • 회원 삭제 /delete-member

문제는 location 위치에 의미를 부여했다.

location은 말그대로 리소스 위치만을 표현해야한다.

 

좋은 예시

  • 회원 목록 조회 /members
  • 회원 조회 /members/{id}
  • 회원 등록 /members/{id}
  • 회원 수정 /members/{id}
  • 회원 삭제 /members/{id}

계층 구조상 상위를 컬렉션으로 보고 복수단어 사용 권장한다.

각 url 행동을 식별하는 것은 HTTP 메서드로 정한다.

GET 리소스 조회

POST  요청 데이터 처리, 주로 등록에 사용한다. 

PUT 리소스 대체, 없으면 생성

PATCH 리소스 부분 변경

DELETE 리소스 삭제

 

 

HTTP 주요 메서드 설명

GET

  • 리소스 조회 시 서버에 전달할 데이터를 쿼리 파라미터 형식으로 보낸다.
  • www.검색사이트.com?query=검색어

POST

  • 요청 데이터 처리, 상당히 애매한 표현일 수도 있으나, 실제로 애매하면 전부 POST로 처리한다. 실제로 삽입수정삭제 전부 POST로 처리하는 곳도 있다.
  • 메시지 바디를 통해 서버로 요청 데이터를 전달한다.

PUT

  • 리소스가 있으면 대체, 아니면 생성
  • POST로도 생성이 가능한데 차이는 PUT은 정확히 리소스 위치를 클라이언트가 정한다.

 

HTTP 메서드의 속성

안전(Safe Methods)

  • 리소스를 변경하는가

멱등(Idempotent Methods)

  • 언제나 몇 번을 호출해도 항상 동일한 결과를 도출하는지.
  • POST가 멱등성을 중족하지 않는다. 이 문제로 PRG(POST-Redirect-GET) 패턴이라는 것이 존재한다.
  • 예컨데 POST요청으로 주문하는데 클라이언트가 실수로 두 번 클릭했다. 주문이 두 번 처리된다.

캐시가능(Cacheable Methods)

  • 응답 결과를 캐시가 가능한지

 

 

 

 

 

 

 

IP(인터넷 프로토콜)

  • IP프로토콜의 한계 비연결성
    • 패킷을 받을 대상이 없거나 서비스 불능 상태여도 패킷 전송
  • 비신뢰성
    • 중간에 패킷 소실, 패킷 순서대로 안올 경우
  • 프로그램 구분
    • 같은 IP인 서버들 구분

 

 

TCP, UDP

TCP 특징

전송 제어 프로토콜(Transmission Control Protocol)

• 연결지향 - TCP 3 way handshake

• 데이터 전달 보증

• 순서 보장

• 신뢰할 수 있는 프로토콜

 

UDP 특징

사용자 데이터그램 프로토콜(User Datagram Protocol)

• 비연결 지향

• 데이터 전달 및 순서가 보장되지 않지만, 단순하고 빠름

• 정리

• IP와 거의 같다. +PORT +체크섬 정도만 추가

• TCP 같은 신뢰성을 유지하려면 어플리케이션에서 작업해야 함

 

 

Port

같은 IP 여러 연결 구분

총 길이는 16bit로 2의 16제곱인 0~ 65535 범위 번호를 가짐

0~1023 은  well-know 포트라 불리며, 미리 지정한 기능을 수행함(약속임)

20,21 FTP

23 telnet

80, 443  HTTP, HTTPS

 

DNS(Domain Name Server)

인간은 IP를 기억하기 어렵다. 규칙성이 없는 숫자 덩어리기 때문이다.

때문에 IP와 매핑된 이름을 부여

 

URI(Uniform Resource Identifier)

Uniform: 리소스 식별하는 통일된 방식

Resource: 자원, URI로 식별할 수 있는 모든 것(제한 없음)

Identifier: 다른 항목과 구분하는데 필요한 정보

 

URL, URN 을 포괄하는 개념

URN은 거의 쓰이질 않음

 

URL(Uniform Resource Locator)

문법  scheme://[userinfo@]host[:port][/path][?query][#fragment]

  • 스키마
    • http, ftp 같은 프로토콜
  • userinfo@
    • 말그대로 인증 정보이다
    • 내가 직접 사용해본 건 ftp를 연결할 때 인증정보를 넣어서 야용해봤다.
  • host
    • IP주소 혹은 매핑된 name 주소이다.
    • www.naver.com
  • port 는 말그대로 port number가 들어간다. 단, well-know포트를 사용한다면 생략가능하다.
    • ftp:100.100.100.100     생략했으니 기본 포트번호인 21번 사용
    • ftp:100.100.100.100:2121       기본포트가 아닌 다른 포트 사용으로 생략 불가
  • path
    • 리소스 경로가 위치한다.
  • query
    • key=value 형태로 값을 넣어 서버에게 정보를 제공한다
  • fragment
    • html 내부 북마크 등에 사용

북마크 예시

 

 

 

 

 

 

 

+ Recent posts