새로운 블로그로 이전하였습니다!

Cross-Origin Resource Sharing

개요

브라우저단에서 컨트롤하는 보안 정책으로

HTTP 통신 중 패킷의 요청 도메인과 응답 도메인이 서로 다를 경우 브라우저에서 강제하는 보안 정책입니다.

HTML 의 href 속성을 통해 다른 도메인에 접근하는 것은 Cross-Origin 정책을 따르기 때문에 문제되지 않지만

스크립트 단에서 Ajax, Fetch API 등을 사용하여 접근하는 것은 기본적으로 Same-Origin 정책을 따르기 때문에

서버에서 허용해주지 않을 경우 접근이 제한됩니다.

 

CORS 정책에 걸리지 않기 위해선 아래 3가지 사항에 따르면 됩니다.

1. 브라우저 옵션에서 CORS 옵션 Diabled

가장 쉽게 해결이 가능하나 가장 효용성이 없는 해결 방법

해당 웹 사이트에 접근하려는 모든 사용자는 CORS 옵션을 끄고 접근해야 합니다.

2. HTTPS 로 변경

실제 운영/배포 예정이라면 필수불가결이긴 합니다만

하지만 본인은 CORS에 대해 알아볼 때 개발 단계, 데모 버전에서만 필요한 정도였어서 아래 Preflgiht 를 구현하였습니다.

3. Preflight 구현

CORS 보안 정책에 따라 Preflight 를 구현하면 일단 HTTP 에서도 서로 다른 도메인끼리 통신이 가능합니다.

 

목적

서버 내에 Side Effect를 일으킬 수 있는 메소드(GET을 제외한 전부)에 접근하기 전에

OPTIONS 메소드를 통해 Preflight (사전 전달)함으로써 본인의 도메인과 Request 보낼 헤더, 메소드에 대해 허가가 되어있는지 확인하고,

서버에서 허가가 떨어지면 그 때 부터 실제 데이터 통신을 주고받게 됩니다.

 

CORS Preflight 구현

Request: Access Control Request

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

HTTP 요청 헤더에 Origin, 요청 시 사용할 헤더, 메소드를 담아서 전송합니다.

실제 로직은 브라우저 단에서 일어나서 HTTP 요청에 오류가 나더라도 CORS 오류 라고만 뱉고,

스크립트 내에서 자세한 오류 확인은 불가능 하여 Dev Tools ( F11 )의 콘솔을 통해 확인해야 합니다.

JavaScript Example

fetch("http://localhost:8080", {
	method: "OPTIONS",
    header: {
    	"Access-Control-Request-Headers": "Content-Type",
        "Access-Control-Request-Method": "POST",
        "Access-Control-Request-Private-Network": "true", <!-- 사설망에서 접근 -->
	});

Response: Access-Control-Allow

Server =>  HTTP 응답 헤더에 접근 제어가 허용된 것들을 담아서 반환합니다.

헤더 목록 Example

Header Name Value 설명
Access-Control-Allow-Origin  <Origin> | * 접근을 허용한 도메인 url
Preflight 이후에도 모든 데이터에 해당 헤더를 담아서 보내야 함
자격 증명이 있는 요청의 경우 와일드 카드 " * "사용이 제한됨
Access-Control-Allow-Headers <Header-name> | [<Header-name>, ] 실제 데이터 통신 시 허용한 헤더
Access-Control-Allow-Methods <method> | [<method>, ] 리소스에 접근 가능한 메소드
Access-Control-Allow-Credentials true 요청 시 credentials 플래그가 true일 경우 응답 허용
해당 헤더가 없으면 브라우저에서 응답을 무시함.
Access-Control-Max-Age <seconds> Preflight 요청 결과를 캐시할 수 있는 시간

 

Java HttpServletResponse Example

public class HttpServletUtils {
	public void initPreflight( HttpServletResponse response ) {
		response.addHeader("Access-Control-Allow-Origin", "http://localhost:3000");
		response.addHeader("Access-Control-Allow-Methods", "POST, OPTIONS");
		response.addHeader("Access-Control-Allow-Headers", "Content-Type");
		response.addHeader("Access-Control-Allow-Credentials", "true");
		response.addHeader("Access-Control-Allow-Private-Network", "true");
		response.setStatus(204);
	}
}

 

이 후 실제 리소스를 요청하면 되는데 서버에서 응답할 때

Access-Control-Allow-Origin 헤더는 필수로 들어가야 합니다.

 

해결하지 못한 문제

 로컬 네트워크에서 다른 도메인 리소스로 접근하는 것은 Preflight 과정 후 원할하게 통신이 되어서 사내 운영서버에 배포하였는데,

외부 네트워크에서 사내 서버에 접근하기 위한 용도의 도메인을 통해 접속 후 Preflight 요청을 하였으나

브라우저 단에서 Request를 막은건지 서버에 Response 로그는 안찍히고 콘솔창에 아래 오류만 떴습니다.

The request client is not a secure context and the resource is in more-private address space `private`

사설 망에서 접근하기 위해 필요하다는 헤더는 다 넣어봤으나 실패..

 

참조

https://developer.chrome.com/blog/private-network-access-preflight/

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

 

복사했습니다!