현대 웹 애플리케이션은 다양한 출처에서 제공하는 리소스를 결합하여 사용자에게 풍부한 경험을 제공합니다. 하지만 보안 문제로 인해 웹 브라우저는 기본적으로 동일 출처 정책(Same-Origin Policy)을 적용하여 다른 도메인 간의 리소스 공유를 제한합니다.
이로 인해 웹 개발자들은 복잡한 보안 문제를 해결하기 위해 Cross-Origin Resource Sharing(CORS)이라는 기술을 사용하게 됩니다.
본 글에서는 웹 보안의 중요한 부분인 CORS에 대해 공부한 내용을 정리하고자 합니다.
Web에서의 리소스(resource)란❓
웹 개발에서 "리소스"는 웹 페이지를 구성하는 다양한 구성 요소를 의미합니다. 이에는 다음이 포함됩니다.
- 문서
- HTML 문서(index.html)는 웹 페이지의 구조와 내용을 정의합니다.
- 스타일시트
- CSS 파일(styles.css)은 웹 페이지의 디자인과 레이아웃을 조정합니다.
- 스크립트
- JavaScript 파일(app.js)은 웹 페이지의 동작을 제어합니다.
- 미디어
- 이미지(logo.png), 비디오(video.mp4), 오디오(audio.mp3) 파일 등이 포함됩니다.
- 폰트
- 웹 페이지에서 사용하는 서체 파일(font.woff, font.ttf)입니다.
- 데이터
- JSON 또는 XML 형식의 API 응답 데이터 등도 리소스에 포함될 수 있습니다.
동일 출처 정책(Same-Origin Policy)이란❓
동일 출처 정책(Same-Origin Policy)은 웹 브라우저에서 기본적으로 적용되는 보안 정책으로, 웹 페이지와 스크립트가 서로 다른 출처(origin)의 리소스에 접근하는 것을 제한합니다.
즉 , 웹 브라우저가 스크립트가 로드된 출처(origin)와 동일한 출처의 리소스만 접근할 수 있도록 제한합니다.
이 정책은 웹 애플리케이션의 보안을 강화하고, 악의적인 공격을 방지하기 위해 설계되었습니다.
동일 출처 정책(SOP)의 주요 개념
출처(Origin)
- 출처(origin)는 웹 페이지가 로드된 기본 주소를 정의합니다. 출처는 다음 세 가지 요소로 구성되며, Protocol + Host + Port 3가지가 같으면 동일 출처(Origin)라고 부릅니다.
- 프로토콜 (예: http, https)
- 도메인 (예: example.com)
- 포트 (예: 80, 443, 8080 등)
- 예를 들어, https://example.com:443과 http://example.com:80 은 포트가 다르기 때문에 서로 다른 출처로 간주됩니다.
동일 출처 정책의 제한
- 브라우저는 동일 출처 정책을 통해 스크립트가 다른 출처의 리소스에 접근하는 것을 차단합니다.
- 예를 들어, https://A.com에서 실행되는 스크립트는 https://B.com의 데이터를
직접 읽거나 조작할 수 없습니다.
- 예를 들어, https://A.com에서 실행되는 스크립트는 https://B.com의 데이터를
- 이는 다음과 같은 보안 문제를 방지하기 위한 것입니다.
- 크로스 사이트 스크립팅(XSS): 악의적인 스크립트가 사용자의 쿠키나 데이터를 훔치는 공격.
- 크로스 사이트 요청 위조(CSRF): 사용자가 모르는 사이에 다른 웹사이트에서 악의적인 요청을 보내는 공격.
정책 적용의 예
- DOM 접근 제한
- 서로 다른 출처의 웹 페이지 간에는 DOM(Document Object Model) 접근이 제한됩니다.
- AJAX 요청 제한
- AJAX를 통해 다른 출처의 서버에 요청을 보내는 것도 제한됩니다.
- 이로 인해 서버 측에서 CORS(Cross-Origin Resource Sharing) 헤더를 사용하여 예외를 설정해야 합니다.
CORS(Cross-Origin Resource Sharing)이란❓
CORS(Cross-Origin Resource Sharing)는 웹 브라우저에서 서로 다른 출처(origin) 간의 리소스에 접근할 수 있도록 브라우저에서 제공하는 보안 기능입니다.
웹 브라우저는 보안상의 기본적으로 이유로 앞서 언급했듯이 동일 출처 정책(Same-Origin Policy)을 적용하여, 웹 페이지가 자신과 다른 출처에서 리소스를 요청하는 것을 제한합니다.
하지만, 현대 웹 애플리케이션에서는 다양한 출처에서 데이터를 주고받는 경우가 많기 때문에, CORS가 이러한 제한을 우회할 수 있도록 도와줍니다.
CORS의 필요성
1. 동일 출처 정책의 한계
- 현대 웹 애플리케이션은 여러 API 서버와 상호작용해야 할 필요가 많으며, 동일 출처 정책만으로는 이러한 기능을 제공하는 데 한계가 있습니다.
- 예를 들어, 프런트엔드 애플리케이션이 특정 API 서버(다른 도메인에 위치)에 데이터를 요청해야 하는 상황에서, 동일 출처 정책이 적용되면 이러한 요청이 차단됩니다. 이때 CORS가 필요합니다.
2. 다양한 출처에서의 리소스 사용
- 현대 웹 애플리케이션은 여러 출처에서 데이터를 수집하고 결합하는데, CORS를 통해 서버가 특정 출처의 요청을 허용함으로써 이를 가능하게 합니다.
3. 보안과 유연성의 제공
- CORS는 보안과 유연성 사이의 균형을 제공합니다.
- CORS가 없다면, 동일 출처 정책에 의해 모든 크로스 도메인 요청이 차단되거나, 반대로 모든 요청이 무조건 허용되면서 보안 문제가 발생할 수 있습니다.
- CORS를 통해 서버는 어떤 출처에서의 요청을 허용할지 세밀하게 제어할 수 있으며, 특정 메서드나 헤더만을 허용할 수 있습니다.
4. Preflight 요청을 통한 사전 검증
- CORS는 Preflight 요청을 통해 잠재적으로 위험한 요청을 사전에 검증할 수 있게 합니다.
- 서버는 클라이언트가 요청을 보내기 전에 해당 요청이 안전한지 확인할 수 있으며, 이를 통해 미리 잘못된 요청을 차단할 수 있습니다.
CORS의 주요 헤더
- Access-Control-Allow-Origin
- 특정 출처 또는 모든 출처(*)에 대한 접근을 허용합니다.
- Access-Control-Allow-Methods
- 허용된 HTTP 메서드(GET, POST, PUT, 등)를 지정합니다.
- Access-Control-Allow-Headers
- 클라이언트가 요청에 포함할 수 있는 허용된 헤더를 지정합니다.
- Access-Control-Allow-Credentials
- 쿠키나 인증 정보의 포함 여부를 결정합니다.
CORS의 동작 원리
CORS는 서버가 응답 헤더를 통해 어떤 출처에서의 요청을 허용할지를 명시함으로써 브라우저가 요청을 처리할 수 있도록 합니다.
CORS의 동작 방식은 크게 3가지로 나눌 수 있습니다.
CORS의 동작 원리 - 단순 요청 (Simple Request)
가장 간단한 CORS 요청은 단순 요청(simple request)이라고 하며, 이 경우 Preflight 요청이 필요하지 않습니다.
단순 요청(Simple Request)은 CORS(Cross-Origin Resource Sharing)에서 기본적으로 허용되는 요청으로, 브라우저가 추가적인 보안 검증 없이 서버와의 교환을 수행할 수 있는 요청을 의미합니다.
1. HTTP 메서드
단순 요청은 다음 HTTP 메서드 중 하나를 사용해야 합니다.
- GET
- 데이터를 요청하는 메서드.
- HEAD
- 데이터를
요청하지 않고 헤더만 요청하는 메서드.
- 데이터를
- POST
- 데이터를 서버에 제출하는 메서드. 단, 특정 Content-Type 값이 있어야 함.
2. Content-Type 헤더
단순 요청에서는 Content-Type 헤더가 다음 세 가지 값 중 하나이어야 합니다.
- application/x-www-form-urlencoded
- 폼 데이터가 URL 인코딩 방식으로 전송되는 형식.
- multipart/form-data
- 파일 업로드와 같이 폼 데이터가 여러 부분으로 나뉘어 전송되는 형식.
- text/plain
- 텍스트 데이터가 전송되는 형식.
이 외의 Content-Type(예: application/json)을 사용하는 경우, 요청은 단순 요청이 아니며, Preflight 요청이 필요합니다.
3. 허용된 헤더
단순 요청에서는 클라이언트가 다음과 같은 기본 헤더만을 포함할 수 있습니다.
- Accept
- 서버가 응답할 수 있는 MIME 타입을 지정합니다.
- Accept-Language
- 클라이언트가 수용 가능한 언어를 지정합니다.
- Content-Language
- 요청의 콘텐츠 언어를 지정합니다.
- DNT
- 추적 방지 요청을 나타냅니다.
- X-Requested-With
- AJAX 요청을 나타내며, 주로 XMLHttpRequest로 사용됩니다.
이 외의 커스텀 헤더를 포함하거나, 표준 헤더 외의 추가적인 헤더를 사용하는 경우, 요청은 단순 요청으로 간주되지 않습니다.
CORS의 동작 원리 - Preflight 요청 (Preflight Request)
Preflight Request는 브라우저는 요청을 예비 요청과 본 요청으로 나누어 서버에 전송합니다.
이때 본 요청을 보내기 전에 브라우저가 서버에 먼저 보내는 예비 요청을 Preflight 요청이라고 합니다. Preflight 요청은 본 요청이 안전한지 확인하는 과정으로, 이때 HTTP 메서드 중 OPTIONS 메서드가 사용됩니다.
동작 방식은 다음과 같습니다.
- 브라우저가 Preflight 요청을 보냄
- OPTIONS 메서드를 사용하여 서버에 사전 요청을 보냅니다.
- 이 요청에는 실제 요청의 메서드와 헤더 정보가 포함됩니다.
- 서버가 Preflight 요청에 응답
- 서버는 요청된 메서드와 헤더를 허용할지 여부를 결정하여 응답합니다.
- 응답 헤더에는 Access-Control-Allow-Origin, Access-Control-Allow-Methods, Access-Control-Allow-Headers 등이 포함됩니다.
- 브라우저가 응답을 확인
- 브라우저는 서버의 응답을 확인하고, 요청이 허용되면 실제 요청을 보냅니다.
요청이 허용되지 않으면, 브라우저는 실제 요청을 차단합니다.
Preflight 요청이 발생하는 조건은 CORS(Cross-Origin Resource Sharing)에서 보안 검증을 위해 설정된 규칙에 따라 다릅니다. 브라우저가 실제 요청을 보내기 전에 Preflight 요청을 보내는 주요 조건은 다음과 같습니다.
1. HTTP 메서드가 단순 요청이 아닐 때
단순 요청(simple request)이 아닌 HTTP 메서드를 사용하는 경우 Preflight 요청이 발생합니다. 단순 요청이란, 다음 조건을 만족하는 요청을 말합니다.
- HTTP 메서드
- GET, HEAD, POST
- Content-Type
- application/x-www-form-urlencoded, multipart/form-data, text/plain
- 추가적인 헤더
- 특정 커스텀 헤더가
포함되지 않음
- 특정 커스텀 헤더가
따라서, PUT, DELETE, PATCH 등의 메서드를 사용하는 요청은 단순 요청이 아니며, 이 경우 Preflight 요청이 필요합니다.
2. 특정 헤더를 사용할 때
커스텀 헤더를 포함하거나 특정 표준 헤더를 사용하는 요청은 Preflight 요청이 발생합니다.
- 커스텀 헤더
- X-Custom-Header, Authorization
- 특정 표준 헤더
- Content-Type이 application/json과 같은 비단순 값인 경우
이와 같은 헤더를 포함하는 요청은 서버가 이러한 헤더를 허용하는지 확인하기 위해 Preflight 요청이 필요합니다.
3. Content-Type이 특정 값이 아닐 때
요청의 Content-Type 헤더가 단순 요청에서 허용된 값이 아닌 경우 Preflight 요청이 발생합니다. 단순 요청에서 허용된 Content-Type은 다음과 같습니다.
- application/x-www-form-urlencoded
- multipart/form-data
- text/plain
이 외의 Content-Type을 사용하는 경우(예: application/json) Preflight 요청이 필요합니다.
CORS의 동작 원리 - 인증된 요청(Credentialed Request)
인증된 요청(Credentialed Request)은 CORS(Cross-Origin Resource Sharing)에서 인증 정보를 포함하는 요청을 의미합니다.
인증 정보에는 쿠키, HTTP 인증 헤더, 또는 클라이언트 인증서 등이 포함될 수 있습니다.
이러한 요청은 보안과 관련된 중요한 정보를 서버와 교환하는 경우가 많기 때문에, 추가적인 CORS 설정이 필요합니다.
Credentialed Request은 CORS의 기본적인 방식이라기 보단 다른 출처간 통신에서 좀 더 보안을 강화하고 싶을 때 사용하는 방법입니다.
인증된 요청의 특징
1. 쿠키와 인증 헤더 포함
- 쿠키
- 요청에 쿠키를 포함시켜 서버와의 세션을 유지하거나, 인증 정보를 전달할 때 사용됩니다.
- HTTP 인증 헤더
- 예를 들어, Authorization 헤더를 통해 사용자 인증 정보를 전송할 수 있습니다.
2. CORS 설정의 변경
- 인증된 요청을 처리하려면 서버는 Access-Control-Allow-Credentials 헤더를 true로 설정해야 합니다.
- 서버는 Access-Control-Allow-Origin 헤더를
*(모든 출처 허용)로 설정할 수 없습니다.- 대신, 요청을 허용할 출처를 명시적으로 설정해야 합니다.
응답 파기 여부 결정권 : 브라우저
CORS(Cross-Origin Resource Sharing) 정책은 웹 애플리케이션의 보안을 유지하기 위해 브라우저에 구현된 스펙입니다.
이 정책의 핵심 포인트는 응답의 파기 여부가 서버가 아니라 브라우저에 의해 결정된다는 것입니다. 이 점에서 중요한 사항을 정리하자면 다음과 같습니다.
1. 브라우저의 CORS 정책
- 출처 비교
- CORS 정책에 따라 브라우저는 요청과 응답의 출처를 비교하여, 응답이 허용된 출처에서 오는 것인지 검토합니다.
- 이 과정은 브라우저 내에서 이루어지며,
서버에서는 이를 제어할 수 없습니다.
- 응답 처리
- 만약 응답이 CORS 정책에 위배되는 경우, 브라우저는 응답을 수신하더라도 해당 데이터를 버리거나 차단합니다.
- 브라우저가 CORS 정책을 위반한다고 판단할 때, 응답을
클라이언트 애플리케이션에 전달하지 않습니다.
2. 서버의 역할
- 서버 설정
- 서버는 Access-Control-Allow-Origin과 같은 CORS 헤더를 통해 응답할 때 특정 출처에서의 요청을 허용하거나 거부할 수 있습니다.
- 그러나, 서버가 설정한 CORS 정책을 위반하는 요청에 대해 서버는 여전히 정상적으로 응답을 반환할 수 있습니다.
- 브라우저의 결정
- 서버가 응답을 보내더라도, 브라우저가 CORS 정책을 확인하고, 정책 위반으로 판단되면 해당 응답을 무시하거나 차단합니다.
3. 브라우저와 서버 간의 차이점
- 브라우저에서의 CORS
- 웹 애플리케이션이 브라우저에서 실행될 때, 브라우저는 CORS 정책에 따라 출처를 비교하고 응답을 검토합니다.
- 이 정책은 보안상의 이유로, 웹 애플리케이션이 다른 출처의 리소스를 안전하게 사용할 수 있도록 보장합니다.
- 서버 간 통신
- 브라우저를 통하지 않는 서버 간의 통신(예: Postman을 사용한 API 테스트)에서는
CORS 정책이 적용되지 않습니다. - 서버 간의 직접 통신에서는 CORS 제한이 없으므로, 응답의 출처를
브라우저처럼 검토하지 않습니다. 이는 주로 테스트와 개발 단계에서 유용합니다.
- 브라우저를 통하지 않는 서버 간의 통신(예: Postman을 사용한 API 테스트)에서는
정리하자면 응답의 파기의 초점은 "브라우저"입니다.
'Security' 카테고리의 다른 글
[Security] XSS란 무엇일까❓ 정의부터 방어 기법까지 (0) | 2024.08.20 |
---|---|
[Security] CSRF란 무엇일까❓ (0) | 2024.08.20 |