52. WIL-03 CORS (항해 37일차)

코이그·2023년 5월 9일

항해99

목록 보기
50/54

Cross-Origin Resource Sharing

추가 HTTP 헤더를 사용하여, 한 출처에서 실행 중인 웹 애플리케이션이 다른 출처의 선택한 자원에 접근할 수 있는 권한을 부여하도록 브라우저에 알려주는 메커니즘

CORS는 브라우저-서버 간의 안전한 교차 출처 요청 및 데이터 전송을 지원함.

Origin: protocol + host + port (ex: https:// www.domain.com :3000)

SOP

  • Same-Origin Policy (동일 출처 정책)
  • *"동일한 출처에서만 리소스를 공유할 수 있다 / 다른 출처의 리소스는 상호작용이 불가능하다"
  • 동일 출처의 기준: Origin

굳이 SOP?

다른 출처의 리소스 간의 상호작용은 위험성이 존재한다. (CSRF나 XSS 등을 이용해 개인 정보 탈취 등)

브라우저의 CORS 기본 동작

  1. 클라이언트가 HTTP 요청을 보낼 때 헤더에 Origin을 담아서 서버로 전달
  2. 서버가 HTTP 응답을 보낼 때 헤더에 Access-Control-Allow-Origin을 담아서 클라이언트로 전달
  3. 클라이언트가 Origin과 서버에서 받은 Access-Control-Allow-Origin 비교
    • 동일하면 다른 출처의 리소스 가져오기 가능 / 아니면 CORS 에러

서버: 클라이언트로 응답을 보낼 때 Access-Control-Allow-Origin을 담아서 보내는게 중요!!

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

예비 요청 (Preflight Request)

브라우저는 서버로 요청을 보내기 전 예비 요청을 보내 현재 요청이 안전한 요청인지 미리 확인함.
예비 요청을 보내는 것을 preflight라고 하고 GET/POST가 아닌 OPTIONS라는 메소드가 사용됨.

예비 요청 구성

  • Origin: 자신의 출처
  • Access-Control-Request-Method: 실제 요청에 사용할 메소드
  • Access-Control-Request-Headers: 실제 요청에 사용할 헤더

예비 요청에 대한 응답 구성

  • Origin: 허용되는 Origin의 목록
  • Access-Control-Allow-Methods: 허용되는 메소드의 목록
  • Access-Control-Allow-Headers: 허용되는 헤더의 목록
  • Access-Control-Max-Age: 예비 요청이 브라우저에 캐시될 수 있는 시간 (초 단위)

문제점

OPTIONS 메소드를 먼저 보냄으로 요청이 안전한지 확인하는 것은 보안적인 측면에서 안전하지만 실제 요청에 걸리는 시간이 늘어나므로 성능적으로는 효율적이지 못하다.

브라우저 캐시를 이용해 Access-Control-Max-Age에 시간을 명시해주면 예비 요청을 캐싱시켜 최적화할 수 있다. (크로미움 기반 브라우저: 최대 2시간 (7200초))

단순 요청 (Simple Request)

예비 요청 없이, 이름처럼 요청을 바로 보내서 서버로부터 받아오는 Access-Control-Allow-Origin이 CORS 정책에 위반하는지 여부를 브라우저가 검사하는 방식.

예비 요청을 생략할 수 있는 3가지 조건

  1. 요청 메소드는 GET/HEAD/POST 중 하나
  2. Accept, Accept-Language, Content-Language, Content-Type, DPR, Downlink, Save-Data, Viewport-Width, Width 헤더일 경우
  3. Content-Type 헤더가 application/x-www-form-urlencoded / multipart/form-data / text/plain 중 하나

조건이 너무 까다롭고 대부분 HTTP API 요청은 text/xml / application/json 으로 통신하기 때문에 보통 API 요청은 예비 요청으로 이루어진다고 생각하면 됨.

인증된 요청 (Credentialed Request)

클라이언트에서 서버에게 자격 인증 정보(Credential)를 실어 요청할 때 사용되는 요청.

자격 인증 정보: 세선 ID가 저장되어있는 쿠키 혹은 Authorization 헤더에 설정하는 토큰 값 등

클라이언트에서 인증 정보를 보내는 설정

credentials: 요청에 인증과 관련된 정보를 담을 수 있게 해주는 옵션.

  • same-origin: 같은 출처 간 요청에만 인증 정보를 담을 수 있음
  • include: 모든 요청에 인증 정보를 담을 수 있음
  • omit: 모든 요청에 인증 정보를 담지 않음
// fetch 메서드
fetch("https://example.com:1234/users/login", {
	method: "POST",
	credentials: "include", // 클라이언트와 서버가 통신할때 쿠키와 같은 인증 정보 값을 공유하겠다는 설정
    body: JSON.stringify({
        userId: 1,
    }),
})

// axios 라이브러리
axios.post('https://example.com:1234/users/login', { 
    profile: { username: username, password: password } 
}, { 
	withCredentials: true // 클라이언트와 서버가 통신할때 쿠키와 같은 인증 정보 값을 공유하겠다는 설정
})

// jQuery 라이브러리
$.ajax({
	url: "https://example.com:1234/users/login",
	type: "POST",
	contentType: "application/json; charset=utf-8",
	dataType: "json",		
	xhrFields: { 
    	withCredentials: true // 클라이언트와 서버가 통신할때 쿠키와 같은 인증 정보 값을 공유하겠다는 설정
    },
	success: function (retval, textStatus) {
		console.log( JSON.stringify(retval));
	}
});

서버에서 인증된 요청에 대한 헤더 설정

  1. Access-Control-Allow-Credentials를 true로 설정
  2. Access-Control-Allow-Origin에 와일드카드 문자("*") 사용 x
  3. Access-Control-Allow-Methods에 와일드카드 문자("*") 사용 x
  4. Access-Control-Allow-Headers에 와일드카드 문자("*") 사용 x

CORS Tutorial

CORS 해결 방법

Chrome 확장 프로그램 이용

프록시 사이트 이용

참고

profile
COYG🔴⚪

0개의 댓글