CORS(Cross Origin Resource Sharing, 교차 출처 리소스 공유)
• 웹에서는 보안을 위해 기본적으로 한 웹 페이지(출처 A)에서 다른 웹 페이지(출처 B)의 데이터를 직접 불러오는 것을 제한하는데 이를 '동일 출처 정책(Same-Origin Policy)' 이라고 한다
• 만약 다른 출처의 리소스를 안전하게 사용하고자 할 경우 CORS 가 등장하며 CORS는 특별한 HTTP 헤더를 통해 한 웹 페이지가 다른 출처의 리소스에 접근할 수 있도록 '허가'를 구하는 방법이라 할 수 있다. 즉, 웹 애플리케이션이 다른 출처의 데이터를 사용하고자 할 때, 브라우저가 그 요청을 대신해서 해당 데이터를 사용해도 되는지 다른 출처에게 물어보는 것이라 할 수 있다
• 출처를 비교하는 로직은 서버에 구현된 스펙이 아닌 브라우저에 구현된 스펙 기준으로 처리되며 브라우저는 클라이언트의 요청 헤더와 서버의 응답헤더를 비교해서 최종 응답을 결정한다
[
- 웹 애플리케이션의 요청:
- 예를 들어, https://myapp.com이라는 웹 애플리케이션이 있고, 이 애플리케이션이 https://api.example.com/data에서 데이터를 가져오려고 한다고 가정해 봅시다.
- 두 URL은 서로 다른 출처이므로 CORS 정책에 의해 브라우저는 이 요청을 그냥 허용하지 않고, 서버에 먼저 물어봐야 합니다.
- 브라우저가 서버에 요청:
- 브라우저는 먼저 https://api.example.com 서버에 "이 요청을 허용할 수 있습니까?"라는 의미의 CORS 요청을 보냅니다. 이때, 이 요청을 "Preflight 요청"이라고도 부르는데, 이는 서버에게 요청을 허용할지 여부를 묻는 사전 요청입니다.
- 서버의 응답:
- 서버는 Access-Control-Allow-Origin이라는 HTTP 헤더를 통해 브라우저에게 이 요청이 허용되는지 알려줍니다.
- 예를 들어, 서버가 "허용"이라고 응답하면, 브라우저는 https://api.example.com/data에서 데이터를 받아올 수 있습니다.
- 만약 서버가 "허용하지 않음"이라고 응답하거나, 아예 응답을 하지 않으면 브라우저는 해당 데이터를 가져오지 않고 요청을 차단합니다
- 서버는 Access-Control-Allow-Origin이라는 HTTP 헤더를 통해 브라우저에게 이 요청이 허용되는지 알려줍니다.
]
• 두개의 출처를 비교하는 방법은 URL의 구성요소 중 Protocol, Host, Port 이 세가지가 동일한지 확인하면 되고 나머지는 틀려도 상관없다
[
출처를 비교하는 기준은 Protocol(프로토콜), Host(호스트, 도메인), 그리고 Port(포트)가 동일한지 확인하는 것입니다.
- Protocol: http와 https는 서로 다른 프로토콜로 간주됩니다.
- Host: example.com과 api.example.com은 서로 다른 도메인입니다.
- Port: 포트가 명시적으로 다르다면(80, 8080 등), 출처가 다른 것으로 간주됩니다.
나머지가 틀려도 상관없다는 의미
출처 비교에서는 오직 Protocol, Host, Port 세 가지가 중요한 요소입니다.
URL의 나머지 구성 요소인 경로(path)나 쿼리 파라미터(query parameters) 등은 출처 비교와 관련이 없습니다.
즉, 경로나 쿼리 파라미터가 다르더라도 출처는 동일할 수 있습니다.
예시
- 같은 출처: 경로가 다르더라도, Protocol이 https, Host가 example.com, Port가 기본 443으로 동일하므로 같은 출처입니다.
- 다른 출처: 프로토콜(https와 http)이 다르기 때문에 다른 출처로 간주됩니다.
- 다른 출처: 도메인(example.com과 api.example.com)이 다르므로 다른 출처입니다.
- 다른 출처: 포트 번호가 다르므로 다른 출처로 간주됩니다.
따라서 출처 비교에서 중요한 것은 Protocol, Host, Port의 일치 여부이고, 경로나 쿼리 파라미터의 차이는 출처 비교에 영향을 미치지 않습니다.
]
• CORS를 Disable 한다는것은?
[
cors().disable()을 사용하면 Spring Security에서 CORS 관련 보안 검사를 비활성화하는 것을 의미
즉, 클라이언트(다른 출처)가 이 서버로 요청을 보낼 때, CORS 규칙에 따른 출처 검사를 하지 않겠다는 의미입니다.
다른 서버에서 요청을 받을 수 있지만, CORS 관련 보안 검사를 하지 않겠다는 말이 됩니다.
CORS 정책을 사용하지않는다는것
== 허용된 출처에서의 요청인지를 검사하지 않겠다는것
== 모든 출처에서 오는 요청을 다 허용하겠다
]
domain-a.com 에서 domain-a.com을 요청하는것은 얼마든지 가능하다.
domain-a.com 에서 domain-b.com을 요청하는것은 Cross-origin 요청으로서 CORS에 제한을 받는다.
https://domain-a.com 의 프론트 엔드 JavaScript 코드가 XMLHttpRequest를 사용하여 https://domain-b.com/data.json을 요청하는 경우 보안 상의 이유로, 브라우저는 스크립트에서 시작한 교차 출처 HTTP 요청을 제한한다.
동일 출처 정책 (Same-Origin Policy, SOP)
**동일 출처 정책(SOP)**은 웹 보안을 위해 브라우저에서 적용하는 기본적인 보안 메커니즘입니다. 이 정책은 한 출처(origin)에서 실행되는 스크립트가 다른 출처의 리소스에 임의로 접근하는 것을 제한합니다.
교차 출처 리소스 공유 (CORS, Cross-Origin Resource Sharing)
CORS는 동일 출처 정책의 예외로, 서버가 명시적으로 교차 출처 요청을 허용할 수 있도록 해주는 메커니즘입니다. 즉, 한 출처에서 다른 출처의 리소스에 접근할 수 있도록 허가를 설정할 수 있는 방식입니다.
CORS의 동작 방식
- 클라이언트(브라우저)가 다른 출처에 리소스를 요청할 때, 서버가 해당 요청을 허용할지 여부를 결정합니다.
- 서버는 HTTP 응답 헤더에 Access-Control-Allow-Origin을 포함시켜, 특정 출처(또는 모든 출처)를 허용할 수 있습니다.
CORS의 예시
- https://domain-a.com의 웹 애플리케이션이 https://domain-b.com에서 데이터를 가져오려 할 때, 동일 출처 정책(SOP)에 의해 기본적으로 차단됩니다.
- 그러나 https://domain-b.com 서버에서 CORS를 설정하여 Access-Control-Allow-Origin: https://domain-a.com이라는 헤더를 추가하면, 브라우저는 이 요청을 허용하게 됩니다.
CORS 요청 흐름
- 클라이언트 요청: 클라이언트가 다른 출처로 교차 출처 요청을 보냅니다.
- 서버 응답: 서버는 응답 헤더에 Access-Control-Allow-Origin을 포함하여 요청을 허용할지 여부를 브라우저에게 알립니다.
- 브라우저 처리: 서버에서 교차 출처 요청이 허용되었다면, 브라우저는 그 요청을 처리하고 데이터를 받아옵니다. 그렇지 않으면 요청이 차단됩니다.
SOP와 CORS의 차이점
- 동일 출처 정책(SOP): 웹 보안의 기본 원칙으로, 다른 출처 간의 리소스 접근을 기본적으로 제한하는 역할을 합니다.
- CORS: SOP의 예외 규칙으로, 서버가 명시적으로 교차 출처 요청을 허용할 수 있는 메커니즘입니다.
CORS는 SOP가 너무 엄격하게 동작할 경우, 이를 유연하게 처리할 수 있게 하여 다양한 API나 리소스를 서로 다른 도메인에서 사용할 수 있도록 돕는 역할을 합니다.
CORS 종류
1. Simple Request
Simple Request 는 예비 요청(Prefilght) 과정 없이 자동으로 CORS가 작동하여 서버에 본 요청을 한 후,
서버가 응답의 헤더에 Access-Control-Allow-Origin 과 같은 값 을 전송하면 브라우저가 서로 비교 후
CORS 정책 위반여부를 검사하는 방식이다
제약 사항
• GET, POST, HEAD 중의 한가지 Method를 사용해야 한다
• 헤더는 Accept, Accept-Language, Content-Language, Content-Type, DPR, Downlink, Save-Data, Viewport-Width Width 만 가능하고 Custom Header 는 허용되지않는다
• Content-type 은 application/x-www-form-urlencoded, multipart/form-data, text/plain 만 가능하다
[ application/json도 안된다. ]
A서비스에서 B서버로 request하면 B 서버가 응답을 보내면서 헤더에 Access-Control-Allow-Origin의 값을 넣어준다.
여기서는 *이므로 모든 출처에 대한 요청을 허가한다는것이다.
브라우저가 해당 응답을 보고 A서비스에 응답을 내려준다.
2. Preflight Request (예비요청)
• 브라우저는 요청을 한번에 보내지 않고, 예비 요청과 본 요청으로 나누어 서버에 전달하는데 브라우저가 예비요청을 보내는 것을 Preflight 라고 하며 이 예비요청의 메소드에는 OPTIONS이 사용된다
• 예비요청의 역할은 본 요청을 보내기 전에 브라우저 스스로 안전한 요청인지 확인하는 것으로
요청 사양이 Simple Request 에 해당하지 않을 경우 브라우저가 Preflight Request 을 실행한다
[Simple Request는 브라우저가 CORS 요청을 보낼 때, 특정 조건을 만족하는 요청에 대해 사전 확인(Preflight) 없이 바로 서버에 요청을 보낼 수 있도록 하는 요청 유형
만약 Simple Request 조건을 하나라도 충족하지 못하면, 브라우저는 안전성을 보장하기 위해 Preflight 요청을 먼저 보냅니다. Preflight 요청은 OPTIONS 메서드를 사용하며, 서버가 요청을 허용할지 확인합니다. ]
브라우저 [ A ]
브라우저가 보낸 요청을 보면 Origin에 대한 정보 뿐만 아니라 예비 요청 이후에 전송할 본 요청에 대한 다른 정보들도 함께 포함되어 있는 것을 볼 수 있다.
이 예비 요청에서 브라우저는 Access-Control-Request-Headers 를 사용하여 자신이 본 요청에서 Content-Type 헤더를 사용할 것을 알려주거나, Access-Control-Request-Method를 사용하여 GET 메소드를 사용할 것을 서버에게 미리 알려주고 있다
서버 [ B ]
서버가 보내준 응답 헤더에 포함된 Access-Control-Allow-Origin: https://security.io 의 의미는
해당 URL 외의 다른 출처로 요청할 경우에는 CORS 정책을 위반했다고 판단하고 오류 메시지를 내고 응답을 버리게 된다
동일 출처 기준
CORS 해결 - 서버에서 Access-Control-Allow 세팅
Access-Control-Allow-Origin
헤더에 작성된 출처만 브라우저가 리소스를 접근할 수 있도록 허용한다
Access-Control-Allow-Origin: *
- 모든 출처의 요청을 허용한다는 뜻입니다.
- 주로 공개된 리소스(API)나, 누구나 접근할 수 있어야 하는 리소스에 적합합니다.
들어갈수 있는값 *[모든경로 ] , https://security.io [특정경로 ]
Access-Control-Allow-Methods
preflight request 에 대한 응답으로 실제 요청 중에 사용할 수 있는 메서드를 나타낸다
• 설정가능한값은 GET,POST,HEAD,OPTIONS, *
서버가 특정 요청 메서드(예: GET, POST, PUT, DELETE)에 대해 교차 출처 요청을 허용할지 여부를 명시하는 HTTP 헤더
- 교차 출처 요청 시 허용할 HTTP 메서드를 서버가 브라우저에 알립니다.
- Preflight 요청(사전 확인 요청)을 통해 브라우저가 서버에 먼저 허용 여부를 확인하게 되며, 서버는 Access-Control-Allow-Methods에 지정된 메서드만 허용합니다.
예시)
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers
preflight request 에 대한 응답으로 실제 요청 중에 사용할 수 있는 헤더 필드 이름을 나타낸다
CORS 응답에서 클라이언트가 접근할 수 있는 응답 헤더 목록을 명시합니다.
브라우저는 기본적으로 Content-Type 등의 일부 헤더만 클라이언트에 노출합니다. 추가적인 헤더에 접근하려면 서버가 Access-Control-Expose-Headers를 통해 허용해야 합니다.
설정가능한값
Origin,Accept,X-Requested-With,Content-Type, Access-Control-Request-Method,Access-Control-Request-Headers, Custom Header, *
예시)
Access-Control-Expose-Headers: X-Total-Count, X-Request-ID
Access-Control-Allow-Credentials
실제 요청에 쿠기나 인증 등의 사용자 자격 증명이 포함될 수 있음을 나타낸다.
Client의 credentials:include 옵션일 경우 true 는 필수
교차 출처 요청에 자격 증명(쿠키, Authorization 헤더 등)을 포함할 수 있는지 여부를 지정합니다.
예시)
Access-Control-Allow-Credentials: true
Access-Control-Max-Age
preflight 요청 결과를 캐시 할 수 있는 시간을 나타내는 것으로 해당 시간동안은 preflight 요청을 다시 하지 않게 된다
Preflight 요청의 응답을 브라우저가 캐시할 수 있는 시간을 초 단위로 지정합니다.
Preflight 요청을 자주 보내지 않게 하여 성능을 최적화하는 데 유용합니다.
예시)
Access-Control-Max-Age: 3600
cors() & CorsFilter
CORS의 사전 요청(pre-flight request)에는 쿠키 (JSESSIONID)가 포함되어 있지 않기 때문에
Spring Security 이전에 처리되어야 한다
사전 요청에 쿠키가 없고 Spring Security가 가장 먼저 처리되면
요청은 사용자가 인증되지 않았다고 판단하고 거부할 수 있다
CORS 가 먼저 처리되도록 하기 위해서 CorsFilter 를 사용할 수 있으며
CorsFilter 에 CorsConfigurationSource 를 제공함으로써 Spring Security 와 통합 할 수 있다
[사전요청은 실제요청이 아니므로 쿠키가 포함되어있지않다. 하지만 Spring Security는 쿠키[JSESSIONID]가 없으므로 인증되지 않은 사용자로 판단할 수 있다. 그러므로 사전요청은 Spring Security 필터 이전에 처리되야한다.]
@Bean
SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
http.cors(cors -> cors.configurationSource(corsConfigurationSource()));
// 커스텀하게 사용할 CorsConfigurationSource 를 설정한다.
// CorsConfigurationSource 를 설정하지 않으면 Spring MVC 의 CORS 구성을 사용한다
return http.build();
}
@Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.addAllowedOrigin("https://example.com"));
configuration.addAllowedMethod("GET","POST"));
configuration.setAllowCredentials(true);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
위에는 커스텀하게 설정한 CorsConfigurationSoucre 예시코드
'인프런 > 스프링 시큐리티 완전 정복 [6.x 개정판]' 카테고리의 다른 글
18) 악용보호 - SameSite (4) | 2024.10.29 |
---|---|
17) 악용보호 - CSRF(Cross Site Request Forgery, 사이트 간 요청 위조) (1) | 2024.10.28 |
15) 예외처리 - exceptionHandling(), ExceptionTranslationFilter (0) | 2024.10.24 |
14) 세션관리 - SessionManagementFilter / ConcurrentSessionFilter (1) | 2024.10.23 |
13)세션관리 - 동시 세션 제어 , 세션 고정 보호, 세션 생성 정책 (1) | 2024.10.22 |
댓글