Access-Control-Allow-Origin가 wildcard(*)일 때 왜 인증 정보를 포함한 요청은 실패하는가 😫

TL;DR

CORS 정책 상 Access-Control-Allow-Origin: * 인 경우 Origin의 제한없이 요청하고 결과를 읽을 수 있지만, 이러한 경우 쿠키를 제거하고 요청하도록 정책이 구성되어 있어서 인증 정보를 포함한 요청에서는 불가능합니다.

만약 쿠키 없이 호출하기 위해선 xhrFields에 withCredentials을 false로 주어 호출하면 됩니다.

xhrFields: {
  withCredentials: false
}

결과적으로 쿠키 기반 인증을 사용하고 있을 때 Access-Control-Allow-Origin: *은 사용자 정보를 탈취하는 관점에선 안전한 형태의 CORS 정책입니다.

이 글의 내용은 TLDR이 전부이고, 제 무지했던 과거와 삽질이 궁금하다면 가볍게 아래 내용 읽어주시면 될 것 같습니다 :D

Problem

테스팅 중 CORS, 즉 Access-Control-Allow-Origin 헤더의 값이 * 로 되어 있길래 이를 이용해서 중요 토큰을 가져오고 그 값을 기반으로 CSRF나 XSS에 활용하려는 코드로 테스트하던 중 이런 에러를 만났습니다.

[ Test code ]

$.ajax({
    url: "http://~~~~~~~",
    type: "post",
    data:
        {"blhablha":"blahblha"}
    ,
    headers: {
        "Accept":"*/*",
        "Accept-Language":"ko-KR;q=1",
        "Content-Type":"application/x-www-form-urlencoded"
    },
    xhrFields: {
        withCredentials: true
    },
    success: function (data) {
        console.info(data);
    }
});

[ Response Header ]

Access-Control-Allow-Origin: *

[ Result ]

교차 출처 요청 차단: 동일 출처 정책으로 인해 ‘https://~~~~~~~’에 있는 원격 자원을 차단하였습니다. (원인: CORS 헤더 ‘Access-Control-Allow-Origin’이 ‘*’이면 자격 증명이 지원되지 않음)

분명히 Access-Control-Allow-Origin은 *로 되어있는데 왜 안되는거지?

혹시나 Firefox 문제인가 싶어 Brave(Chromium engine 기반)에서 테스트해도 결과는 동일했습니다.

Access to XMLHttpRequest at ‘https://~~~~~’ from origin ‘http://127.0.0.1’ has been blocked by CORS policy: The value of the ‘Access-Control-Allow-Origin’ header in the response must not be the wildcard ‘*’ when the request’s credentials mode is ‘include’. The credentials mode of requests initiated by the XMLHttpRequest is controlled by the withCredentials attribute.

제가 잘 몰랐었던 부분인데요. 찾아보니 CORS 사용 시 ACAO 헤더에 wildcard로 명시되는 경우 쿠키를 사용하지 못하도록 제제하고 있는 것입니다. 관련 내용은 아래 링크에서 확인할 수 있습니다.

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

결국은 제 코드 기준으론 이 부분이 문제가 됬었네요.

xhrFields: {
  withCredentials: true
}

References

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