CORS

지인·2023년 8월 2일
0

CS

목록 보기
2/6
post-custom-banner

🐰 CORS

교차 출처 리소스 공유 (Cross-Origin Resource Sharing)

  • 추가 HTTP 헤더를 사용하여, 한 출처에서 실행중인 웹 애플리케이션이 다른 출처의 선택한 자원에 접근할 수 있는 권한을 부여하도록 브라우저에 알려주는 체제
  • 웹 애플리케이션은 리소스가 자신의 출처와 다를 때 교차 출처 HTTP 요청을 실행한다.
  • 쉽게말해 웹사이트가 다른 웹사이트의 자원을 사용할 수 있도록 허용하는 체제이다.
    • 예를 들어, A.com이라는 웹사이트에서 B.com이라는 웹사이트의 이미지를 사용하려면 B.com에서 CORS를 설정해야 한다. 이렇게 하면 브라우저가 A.com에서 B.com의 이미지를 안전하게 가져올 수 있다.

출처(Origin)

  • 출처는 Protocol, Host 그리고 Port까지 모두 합친 것을 의미한다.
    브라우저의 개발자 도구 콘솔에서 Location 객체가 가지고 있는 origin 프로퍼티에 접근하여 현재 출처를 알아낼 수도 있다.

작동 원리

  • 다른 출처의 리소스를 요청할 때 HTTP 프로토콜을 사용하여 요청을 보내게 되는데, 이때 브라우저는 요청헤더에 Origin 이라는 필드에 요청을 보내는 출처를 함께 담아서 보낸다.
Origin: https://velog.io
  • 이후 서버가 요청에 대한 응답을 할 때 응답 헤더의 Access-Countrol-Allow-ORigin 이라는 값에 접근이 허용된 출처를 알려주고 응답을 받은 브라우저는 클라이언트가 보낸 요청의 Origin 과 서버가 보내준 응답을 비교하여 이 응답이 유효한 응답인지 아닌지 결정한다.

  • CORS 요청에는 단순 요청실행 전 요청 두 가지 유형이 있다.

1. 단순 요청 (Simple Request)

  • 예비 요청(Prefilght)을 생략하고 바로 서버에 본 요청을 한후, 서버가 이에 대한 응답의 헤더에 Access-Control-Allow-Origin 헤더를 보내주면 브라우저가 CORS 정책 위반 여부를 검사하는 검사하는 방식이다.

  • 단순요청은 특정한 조건을 만족하는 경우에만 성립할 수 있다.

    1. 요청 메서드(Method)는 GET, HEAD, POST 중 하나여야 한다.

    2. Content-Type 헤더가 application/x-www-form-urlencoded, multipart/form-data, text/plain중 하나여야한다. 아닐 경우 예비 요청으로 동작된다.

    3. Accept, Accept-Language, Content-Language, Content-Type, DPR, Downlink, Save-Data, Viewport-Width, Width 헤더일 경우 에만 적용된다.

  • 예를 들어, 브라우저에서 웹사이트 A에서 웹사이트 B로 데이터를 가져오려고 할 때, 위의 조건을 모두 만족하면 해당 요청은 "단순 요청"으로 간주되어 별도의 사전 요청(예비 요청)없이 바로 실행된다.

2. 예비 요청 (Preflight Request)

  • 예비 요청과 본 요청으로 나누어서 서버로 전송한다.
    이때 브라우저가 예비요청을 보내는 것을 Preflight라고 부르며 예비요청은 OPTIONS 메소드를 사용한다. 예비 요청의 역할은 본 요청을 보내기전 브라우저가 요청을 보내는 것이 안전한지 확인하는 것이다.

  • 자바스크립트의 fetch API를 통해 브라우저에게 리소스를 받아오게 하면 브라우저는 서버로 예비요청을 먼저 보내고 서버는 이 예비요청에 대한 응답으로 어떤 것을 허용하고 어떤것을 금지하고 있는지에 대한 정보를 담아서 브라우저로 다시 보내준다.

  • 이후 브라우저는 보낸 요청과 서버가 응답해준 정책을 비교하여 해당 요청이 안전한지 확인하고 본 요청을 보내게 된다. 이후 서버가 본 요청에 대한 응답을 하면 최종적으로 이 응답 데이터를 자바스립트로 넘겨준다.

3. 인증된 요청 (Credentialed Request)

  • 다른 출처간 통신에서 좀 더 보안을 강화하고 싶을 때 사용한다.

  • 기본적으로 제공하는 리소스 요청 API인 XMLHttpRequest 또는 fetch는 별도의 옵션 없이 브라우저의 쿠키 정보나 인증과 관련된 헤더를 함부로 요청에 담지 않는다. 이때 요청에 인증과 관련되 정보를 담을 수 있게 해주는 옵션이 credentials옵션이다.

옵션값설명
same-origin(기본값)같은 출처간 요청에만 인증 정보를 담을 수 있다
include모든 요청에 인증 정보를 담을 수 있다
omit모든 요청에 인증 정보를 담지 않는다
  • 만약 same-origin 이나 include와 같은 옵션을 사용하여 리소스 요청에 인증 정보가 포함된다면, 브라우저는 다른 출처의 리소를 요청할 때 Access-Control-Allow-Origin만 확인하는 것이 아니라 다른 조건을 추가로 검사한다.

  • 요청에 인증정보가 담겨있는 상태에서 다른 출처의 리소스를 요청하게 되면 브라우저는 CORS정책 위반 여부를 검사하는 룰에 다음 두가지를 추가하게 된다.

  • Access-Control-Allow-Origin 에는 모든 요청을 허용하는*을 사용할 수 없으며, 명시적인 URL이어야 한다
    응답 헤더에는 반드시 Access-Control-Allow-Credentials:true가 존재해야 한다


🐾 허용하는 방법

  • 서버 측에서 적절한 설정을 해주어야 한다.

  • 서버에서 Access-Control-Allow-Origin 헤더에 허용할 출처를 기재해서 클라이언트에 응답하면 된다.

Access-Control-Allow-Origin: https://example.com

🐾 Access-Control-Allow-Origin 헤더 세팅

  • JSP / Servlet
import javax.servlet.*;

public class CORSInterceptor implements Filter {

    private static final String[] allowedOrigins = {
            "http://localhost:3000", "http://localhost:5500", "http://localhost:5501",
            "http://127.0.0.1:3000", "http://127.0.0.1:5500", "http://127.0.0.1:5501"
    };

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;

        String requestOrigin = request.getHeader("Origin");
        if(isAllowedOrigin(requestOrigin)) {
            // Authorize the origin, all headers, and all methods
            ((HttpServletResponse) servletResponse).addHeader("Access-Control-Allow-Origin", requestOrigin);
            ((HttpServletResponse) servletResponse).addHeader("Access-Control-Allow-Headers", "*");
            ((HttpServletResponse) servletResponse).addHeader("Access-Control-Allow-Methods",
                    "GET, OPTIONS, HEAD, PUT, POST, DELETE");

            HttpServletResponse resp = (HttpServletResponse) servletResponse;

            // CORS handshake (pre-flight request)
            if (request.getMethod().equals("OPTIONS")) {
                resp.setStatus(HttpServletResponse.SC_ACCEPTED);
                return;
            }
        }
        // pass the request along the filter chain
        filterChain.doFilter(request, servletResponse);
    }

    private boolean isAllowedOrigin(String origin){
        for (String allowedOrigin : allowedOrigins) {
            if(origin.equals(allowedOrigin)) return true;
        }
        return false;
    }
}
  • Spring
// 스프링 서버 전역적으로 CORS 설정
@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
        	.allowedOrigins("http://localhost:8080", "http://localhost:8081") // 허용할 출처
            .allowedMethods("GET", "POST") // 허용할 HTTP method
            .allowCredentials(true) // 쿠키 인증 요청 허용
            .maxAge(3000) // 원하는 시간만큼 pre-flight 리퀘스트를 캐싱
    }
}
// 특정 컨트롤러에만 CORS 적용하고 싶을때.
@Controller
@CrossOrigin(origins = "*", methods = RequestMethod.GET) 
public class customController {

	// 특정 메소드에만 CORS 적용 가능
    @GetMapping("/url")  
    @CrossOrigin(origins = "*", methods = RequestMethod.GET) 
    @ResponseBody
    public List<Object> findAll(){
        return service.getAll();
    }
}

참고

교차 출처 리소스 공유 (CORS)

[WEB] CORS 개념 정리

악명 높은 CORS 개념 & 해결법 - 정리 끝판왕

profile
열쩡
post-custom-banner

0개의 댓글