CORS 해결 방법

Park sang woo·2024년 7월 26일

CS스터디

목록 보기
10/25

✅ Chrome 확장 프로그램 이용

Chrome 확장 프로그램 설치

프로그램 활성화시키면 로컬환경에서 API 테스트 시, CORS 문제 해결.



✅ 프록시 사이트 이용


프론트에서 직접 서버에 리소스 요청을 했더니 서버에서 따로 설정을 안 해줘서 CORS 에러가 발생한다면 모든 출처를 허용한 서버 대리점을 통해 요청을 진행됩니다.

하지만 무료 프록시 서버 대여 서비스들은 악용 사례 때문에 API 요청 횟수 제한을 두어 실전에서는 사용이 어렵습니다.

실전에서는 직접 프록시 서버를 구축하여 사용.



서버에서 Access-Control-Allow-Origin 헤더 세팅

직접 서버에서 HTTP 헤더 설정을 통해 출처를 허용하게 설정하는 것이 가장 정석적인 해결책!!

각 서버의 문법에 맞게 HTTP 헤더를 추가해주면 됩니다.

CORS에 연관된 HTTP 헤더 값 종류

# 헤더에 작성된 출처만 브라우저가 리소스를 접근할 수 있도록 허용함.
# * 이면 모든 곳에 공개되어 있음을 의미한다. 
Access-Control-Allow-Origin : https://naver.com

# 리소스 접근을 허용하는 HTTP 메서드를 지정해 주는 헤더
Access-Control-Request-Methods : GET, POST, PUT, DELETE

# 요청을 허용하는 해더.
Access-Control-Allow-Headers : Origin,Accept,X-Requested-With,Content-Type,Access-Control-Request-Method,Access-Control-Request-Headers,Authorization

# 클라이언트에서 preflight 의 요청 결과를 저장할 기간을 지정
# 60초 동안 preflight 요청을 캐시하는 설정으로, 첫 요청 이후 60초 동안은 OPTIONS 메소드를 사용하는 예비 요청을 보내지 않는다.
Access-Control-Max-Age : 60

# 클라이언트 요청이 쿠키를 통해서 자격 증명을 해야 하는 경우에 true. 
# 자바스크립트 요청에서 credentials가 include일 때 요청에 대한 응답을 할 수 있는지를 나타낸다.
Access-Control-Allow-Credentials : true

# 기본적으로 브라우저에게 노출이 되지 않지만, 브라우저 측에서 접근할 수 있게 허용해주는 헤더를 지정
Access-Control-Expose-Headers : Content-Length


⚒️ Node.js 세팅

서버에 response 헤더 값으로 Access-Control 설정을 해줌.

var http = require('http');

const PORT = process.env.PORT || 3000;

var httpServer = http.createServer(function (request, response) {
    // Setting up Headers
    response.setHeader('Access-Control-Allow-origin', '*'); // 모든 출처(orogin)을 허용
    response.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE'); // 모든 HTTP 메서드 허용
    response.setHeader('Access-Control-Allow-Credentials', 'true'); // 클라이언트와 서버 간에 쿠키 주고받기 허용

    // ...

    response.writeHead(200, { 'Content-Type': 'text/plain' });
    response.end('ok');
});

httpServer.listen(PORT, () => {
    console.log('Server is running at port 3000...');
});

Access-Control-Allow-Origin 헤더 값으로 *를 사용하면 보안이 허술해지기 때문에 출처를 직접 명시해주는 것이 정석!!

response.setHeader('Access-Control-Allow-origin', 'https://velog.io');


⚒️ 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();
    }
}


⚒️ AWS (S3 호스팅)

  • S3 콘솔 메뉴에 들어가 버킷을 선택.
  • 권한 탭을 선택.
  • 교차 출처 리소스 공유 창에서 [편집]을 선택.
  • 텍스트 상자 아래 JSON CORS 규칙을 입력.

[
  {
    "AllowedHeaders": [
      "Authorization"
    ],
    "AllowedMethods": [
      "GET",
      "HEAD"
    ],
    "AllowedOrigins": [
      "http://www.example.com"
    ],
    "ExposeHeaders": [
      "Access-Control-Allow-Origin"
    ]
  }
]


⚒️ Apache 세팅

<IfModule mod_headers.c>
	Header set Access-Control-Allow-Origin "*"
</IfModule>


⚒️ Tomcat

<filter>
    <filter-name>CorsFilter</filter-name>
    <filter-class>org.apache.catalina.filters.CorsFilter</filter-class>
    <init-param>
        <param-name>cors.allowed.origins</param-name>
        <param-value>*</param-value>
    </init-param>
    <init-param>
        <param-name>cors.allowed.methods</param-name>
        <param-value>GET,POST,HEAD,OPTIONS,PUT,DELETE</param-value>
    </init-param>
    <init-param>
        <param-name>cors.allowed.headers</param-name>
        <param-value>Content-Type,X-Requested-With,accept,Origin,Access-Control-Request-Method,Access-Control-Request-Headers</param-value>
    </init-param>
    <init-param>
        <param-name>cors.exposed.headers</param-name>
        <param-value>Access-Control-Allow-Origin,Access-Control-Allow-Credentials</param-value>
    </init-param>
    <init-param>
    	<!-- 쿠키 통신을 안하는데 이걸 true로 하면 4XX 서버 에러가 뜬다 -->
        <param-name>cors.support.credentials</param-name>
        <param-value>false</param-value> 
    </init-param>
    <init-param>
        <param-name>cors.preflight.maxage</param-name>
        <param-value>10</param-value>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>CorsFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>


⚒️ Nginx

nginx.conf 파일 안에 location / 부분에다가 add_header 값으로 헤더 설정을 추가

location / {
    root html;
    add_header 'Access-Control-Allow-Origin' '*';
    index  index.html index.htm;
}


CORS 정리
CORS가 뭔지, SOP 등이 뭔지는 알고 있지만 구체적인 깊이와 명확한 해결 방안에 대한 회고가 필요하다고 느껴 다시 블로그를 작성



CORS와 Next.js

스프링에서는 CORS 보안이 취약할 수 있다는 의견을 들어 Next.js에서 어떻게 해결하는지 프로젝트를 통해서 적용해보고 작성해보자.






Reference

🖇️ CORS 해결 방법

profile
일상의 인연에 감사하라. 기적은 의외로 가까운 곳에 있을지도 모른다.

0개의 댓글