1. Single Origin Policy: 동일 출처만 허용1-1. 목적1-2. 브라우저에 의해 제한되는 Cross-Origin 접근의 종류1-3. Opaque Requests: 과거 시대의 교차 출처에 대한 접근 방법1-4. JSONP: 과거의 SOP 회피 방법 - 서드파티 호출 방법2. CORS: 예외적으로 Cross-Origin 접근 허용2-1. 목적2-1. 명시적 허용2-2. 명시적 허용 없이도 요청 가능한 Simple Request2-3. Preflight 요청2-4. Opaque Requests 모드3. Client에서의 CORS 활용3-1. HTML에서의 CORS 활용3-2. fetch에서의 CORS 활용4. 주요 참고 출처
1. Single Origin Policy: 동일 출처만 허용
1-1. 목적
- iframe으로 외부 서비스를 로드하고, JavaScript 사용자의 정보를 추출하면, 추출이 되지 않을까요?
- 이러한 공격을 막기 위해 브라우저는 기본적으로 교차 출처(Origin)에 대한 접근을 대부분 비허용합니다.
- SOP는 JavaScript가 등장하면서부터 존재했던 정책으로, 1996년부터 널리 채택되었습니다.
- ‘출처’에는 두 가지 종류가 있습니다.
- 동일 출처(Same Origin)
- 교차 출처(Cross Origin)
- Origin = URI Scheme + Authority
- URI Scheme =
https://
- Authority =
Domain Name + Port
- Domain Name =
subdomain.registered-domain.tld
1-2. 브라우저에 의해 제한되는 Cross-Origin 접근의 종류
- AJAX 요청
- iframe 접근
- Web Storage, Indexed DB
- 타 출처의 자원을 로드하는 Canvas에 대한 JS 접근
1-3. Opaque Requests: 과거 시대의 교차 출처에 대한 접근 방법
- 서드파티 자원은 과거에도 로드가 가능했습니다.
- 단, 로드만 가능하고, 해당 자원에 대해 JavaScript로 접근할 수는 없습니다.
- 만약 JavaScript를 로드하는 경우 실행까지 되었습니다.
1-4. JSONP: 과거의 SOP 회피 방법 - 서드파티 호출 방법
- JSONP는 JSON with Padding의 약자로, 2006년부터 널리 사용되었습니다.
- 이는 서드 파티 JavaScript를 불러오면 JavaScript가 실행되는 원리를 활용한 것입니다.
- 호출할 함수의 이름을 전달하는 방법을 사용했습니다.
- 서드파티 서버에서 해당 함수의 이름으로 호출하는 Script를 만들어 반환했고, 브라우저는 실행했습니다.
- 이러한 방법은 서드 파티 API를 CORS 없이 활용하는데 사용될 수 있습니다.
- JSONP 호출용 URL:
<script src="http://example.com/data?callback=handleData"></script>
- 응답 JavaScript:
callback(/* …서드파티에서 제공하는 데이터 */);
- 한계
- JSONP는 script src를 활용했기 때문에, GET 요청만이 가능했습니다.
- 또한 Script를 실행하기 때문에 서드 파티 서버를 온전히 신뢰해야만 했습니다.
- CSRF 공격을 막을 수 없었기 때문에, 사용자 인증이 필요한 기능에는 사용되지 않아 단순한 정보 제공만 가능했습니다.
2. CORS: 예외적으로 Cross-Origin 접근 허용
2-1. 목적
- CORS는 Cross Origin Resource Sharing의 약자입니다.
- CORS는 2009년 초안이 발표되고, 2010년대 초반부터 브라우저들에서 채택되었습니다.
- 명확한 개발 목적을 소개하는 문서는 없지만, 서드 파티 사용의 확산과 JSONP의 보안적인 한계를 개선해 Web 생태계를 더 발전시키기 위함이었을 것 같습니다.
2-1. 명시적 허용
- 서버는 Response Header를 통해 명시적으로 Cross-Origin에 대한 요청과 응답 내용 조회를 허용할 수 있습니다.
- 이를 위해 4개의 Header가 존재합니다.
- Access-Control-Allow-
- Origin: 특정 Origin 혹은
*
값 - Methods: 명시적으로 허용할 Method 목록 (
,
로 구분) - Headers: 명시적으로 허용할 Headers 목록 (
,
로 구분) - Credentials:
true
혹은 미전송
- 서버는 위 Response Header를 Preflight 혹은 Simple Request 요청의 응답에 포함시켜 반환합니다.
- 브라우저는 서버로부터 Access-Control-Allow Header 값에 따라서 JavaScript에서 요청을 실행하고 응답을 읽을 수 있게 허용합니다.
2-2. 명시적 허용 없이도 요청 가능한 Simple Request
- 아래의 조건(CORS-safelisted)을 만족하는 요청을 Simple Request라고 합니다.
- Simple Request는 Cross-Origin이어도 명시적 허용 없이 요청이 가능합니다.
- Method:
GET | POST | HEAD
- Request Headers:
Accept, Accept-language, Content-language
Content-Type
의 경우form-urlencoded, multipart, text/plain
일 때.
- 단, 명시적 허용 없이도 가능한 것은 요청 뿐입니다.
- Response Header로 허용받지 못하면 Opaque Requests로 간주되어 응답에 접근할 수 없습니다.
- 보안 유의 사항
- 쿠키가 포함되어도 Simple Request이기 때문에 보안에 유의해야 합니다.
2-3. Preflight 요청
- Simple Request에 해당하지 않는 요청은 사전의 Preflight 요청이 요구됩니다.
- Simple Request와는 다르게, 명시적 허용이 없다면 브라우저에 의해 요청 자체가 거부됩니다.
- Preflight 요청은 OPTIONS 메소드와 Access-Control-Request-{Method,Headers}로 구성됩니다.
- 서버는 Preflight 요청에 대한 캐싱 시간을 지정할 수 있습니다.
- Access-Control-Max-Age 헤더를 활용합니다. (기본값
0
, 캐싱 없음.) - 현실적으로 JSON API를 대부분 사용하므로 Preflight 캐싱은 사실상 필수적입니다.
2-4. Opaque Requests 모드
- 현재도 CORS 등장 이전 시기와 같이 Opaque Requests가 존재합니다.
- 대상
- script, link, img, object/embed, font, …
- 단, 명시적인
crossorigin=use-credentials
속성이 없다면 쿠키를 보내지 않습니다.
3. Client에서의 CORS 활용
3-1. HTML에서의 CORS 활용
- 기본적으로 Opaque Requests 모드로 작동하기 때문에 CORS를 무시하고 동작합니다.
- 이 경우, 쿠키를 전송하지 않으며, 쿠키 전송이 필요한 경우
crossorigin=use-credentials
프로퍼티를 명시해야 합니다. - 이 방식에서 오류가 발생하는 경우 오류 메시지 마저 ‘Script Error’라고 짧게만 제공합니다.
- 만약 ESM(
type=”module”
)을 사용 중이라면 기본적으로 CORS 모드로 동작합니다. - Simple Request이더라도 CORS허용 응답을 받지 못하면 로드되지 않습니다.
- 이는 ESM이 처음부터 보안에 신경을 쓰고 만들어졌기 때문입니다.
3-2. fetch에서의 CORS 활용
- mode 옵션이 있습니다.
- mode:
cors | no-cors | same-origin
- cors: CORS 요청을 그대로 사용합니다.
- no-cors: ‘No-CORS’ 방식으로 요청 전송합니다.
- Simple Request만 가능하며, 응답을 읽을 수 없습니다.
- same-origin: cross-origin 요청 발생 시 throw시킵니다.
- credentials 옵션이 있습니다.
- credentials:
include | same-origin | omit
- 인증 정보(Cookie, Authorization 헤더)를 전송하는 cross-origin 요청을 위해서는 명시적으로
include
옵션을 사용해야 합니다. - 참고: Cookie 인증 방식 사용 시에는, Simple Request인 경우 Preflight가 필요 없습니다.
4. 주요 참고 출처
- 도서: 프런트엔드 개발을 위한 보안 입문
- ‣
- ‣
- ‣
- ‣