허용적인 CORS 정책을 갖는 것은 보안에 민감합니다. 과거에는 다음과 같은 취약점이 있었습니다.
브라우저의 동일 출처 정책 은 기본적으로, 또 보안상의 이유로 자바스크립트 프론트엔드가 출처가 다른(도메인, 프로토콜, 포트) 리소스에 대한 교차 출처 HTTP 요청을 수행하는 것을 방지합니다. 요청된 대상은 CORS 라고 불리는 추가적인 HTTP 헤더를 Response 에 추가할 수 있고, 이 헤더는 브라우저의 접근 제어 정책을 변경하거나 동일 출처 정책을 완화하는 지시어 역할을 합니다.
스스로에게 자문하세요
- 지정된 원본을 신뢰하지 않음(예시: Access-Control-Allow-Origin: untrustedwebsite.com).
- 접근 제어 정책이 완전히 비활성화됨: Access-Control-Allow-Origin: *
- 접근 제어 정책이 오리진 헤더와 같은 사용자 제어 입력에 의해 동적으로 정의됨.
이러한 질문에 예라고 답하는 경우 문제가 발생할 수 있습니다.
추천되는 안전한 코딩 관습
- Access-Control-Allow-Origin 헤더는 신뢰할 수 있는 원본 및 특정 리소스에 대해서만 설정해야 합니다.
- Access-Control-Allow-Origin 헤더에서 선택되고 신뢰할 수 있는 도메인만 허용합니다. 블랙리스트에 추가하거나 모든 도메인을 허용하는 것보다 도메인을 화이트리스트에 추가하는 것이 선호됩니다. (* 와일드카드를 사용하거나 확인하지 않고 맹목적으로 Origin 헤더 콘텐츠를 반환하지 마십시오).
보안에 민감한 코드의 예
자바 서블릿 프레임워크 :
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setHeader("Content-Type", "text/plain; charset=utf-8");
resp.setHeader("Access-Control-Allow-Origin", "*"); // Sensitive
resp.setHeader("Access-Control-Allow-Credentials", "true");
resp.setHeader("Access-Control-Allow-Methods", "GET");
resp.getWriter().write("response");
}
스프링 MVC 프레임워크 :
@CrossOrigin // 민감한 코드
@RequestMapping("")
public class TestController {
public String home(ModelMap model) {
model.addAttribute("message", "ok ");
return "view";
}
}
CorsConfiguration config = new CorsConfiguration();
config.addAllowedOrigin("*"); // 민감한 코드
config.applyPermitDefaultValues(); // 민감한 코드
class Insecure implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*"); // 민감한 코드
}
}
- 사용자 제어 오리진 :
public ResponseEntity<String> userControlledOrigin(@RequestHeader("Origin") String origin) {
HttpHeaders responseHeaders = new HttpHeaders();
responseHeaders.add("Access-Control-Allow-Origin", origin); // 민감한 코드
return new ResponseEntity<>("content", responseHeaders, HttpStatus.CREATED);
}
규칙을 준수한 해결책
자바 서블릿 프레임워크 :
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setHeader("Content-Type", "text/plain; charset=utf-8");
resp.setHeader("Access-Control-Allow-Origin", "trustedwebsite.com"); // 규칙을 준수한 코드
resp.setHeader("Access-Control-Allow-Credentials", "true");
resp.setHeader("Access-Control-Allow-Methods", "GET");
resp.getWriter().write("response");
}
스프링 MVC 프레임워크 :
@CrossOrigin("trustedwebsite.com") // 규칙을 준수한 코드
@RequestMapping("")
public class TestController {
public String home(ModelMap model) {
model.addAttribute("message", "ok ");
return "view";
}
}
CorsConfiguration config = new CorsConfiguration();
config.addAllowedOrigin("http://domain2.com"); // 규칙을 준수한 코드
class Safe implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("safe.com"); // 규칙을 준수한 코드
}
}
- 허용된 리스트를 통해 검증된 사용자 제어 오리진 :
public ResponseEntity<String> userControlledOrigin(@RequestHeader("Origin") String origin) {
HttpHeaders responseHeaders = new HttpHeaders();
if (trustedOrigins.contains(origin)) {
responseHeaders.add("Access-Control-Allow-Origin", origin);
}
return new ResponseEntity<>("content", responseHeaders, HttpStatus.CREATED);
}
참고
- OWASP Top 10 2021 Category A5 - Security Misconfiguration
- OWASP Top 10 2021 Category A7 - Identification and Authentication Failures
- developer.mozilla.org - CORS
- developer.mozilla.org - Same origin policy
- OWASP Top 10 2017 Category A6 - Security Misconfiguration
- OWASP HTML5 Security Cheat Sheet - Cross Origin Resource Sharing
- MITRE, CWE-346 - Origin Validation Error
- MITRE, CWE-942 - Overly Permissive Cross-domain Whitelist
- SANS Top 25 - Porous Defenses
If you like SONARKUBE, don’t forget to give me a star.