CORS

·2022년 6월 18일
0

SOP(Same Origin Policy): 동일 출처 정책

  • 브라우저는 기본적으로 사용자가 방문하는 사이트를 신뢰하지 않기 때문에 다른 출처(Origin)으로 부터 받아온 데이터는 차단한다.
  • 차단하는 이유로는 브라우저는 토큰이나 쿠키 등과 같은 사용자의 정보와 관련된 데이터를 저장하는데 해커가 이를 탈취해(CSRF, XSS) 인증 요청에 이를 실어보내 얻은 정보를 가지게 된다면 심각한 문제가 될것이다. 그리고 이러한 문제를 막기 위해 등장한 정책이 동일 출처 정책이다.

    즉 SOP란 자신과 동일한 출처(Origin)의 서버로 부터만 데이터를 요청하여 받을 수 있도록 하는 정책이다.

Origin: 출처

  • URL의 Protocol, Host, Port를 통해 동일한 출처(Same Origin)인지 다른 출처(Cross Origin)인지를 판단할 수 있다.
https://www.google.com:443
https://www.google.com:443/search?q=CORS 	: 동일출처
http://www.google.com:443					: 다른출처
https://www.naver.com:443					: 다른출처
https://www.google.com:123				: 다른출처

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

  • 추가 HTTP 헤더를 사용해 한 출처에서 실행 중인 웹 애플리케이션이 다른 출처의 자원에 접근할 수 있는 권한을 부여하도록 브라우저에 알려주는 체제이다.

CORS 접근 제어 시나리오

프리플라이트 요청(Preflight Request)

: Option 메서드를 통해 다른 도메인의 리소스에 요청이 가능한지 확인하고 가능하다면 실제 요청을 보낸다.

  • Preflight Request
    :실제 요청의 메서드를 전송해 실제 요청을 전송할 때 POST 메서드로 전송된다는 것을 알리고, 실제요청의 추가제더를 전송하여 X-PINGOTHER 와 Content-Type 의 사용자 정의 헤더가 함께 전송된다는 것을 서버에 알린다.
  • Preflight Response
    : 서버가 메서드와 헤더를 받을 수 있음을 알려주고, Prefilgth 응답 캐시기간을 알려준다. 캐시기간은 이전과 똑같은 요청이 올때 사전 요청 없이 바로 본 요청을 보낼 수 있게 한다.

단순 요청(Simple Request)

: 요청을 보내면 즉시 cross origin인지 확인한다.

  • 단순 요청을 위해서는 조건을 충족해야한다.
    - 메서드는 GET, POST, HEAD 중 하나
    - 헤더는 Accept,Accept-Language, Content-Language, Content-Type 만 허용
    - Content-Type 헤더는 application/x-www-form-urlencoded,multipart/form-data,text/plain만 허용

인증정보 포함 요청(Credentialed Request)

: 인증 관련 헤더를 포함할 때 사용하는 요청이다. 쿠키나 토큰을 전달하고 싶을 때 사용하는 요청이다.

  • 클라이언트 측
    : credentials : include
  • 서버 측
    :Access-Control-Allow-Credentials: true
    (단 Access-Control-Allow-Origin:*은 되지 않는다.)

CORS 해결방법

  • Access-Control-Allow-Origin 응답 헤더 세팅
    : 서버측 응답에서 접근 권한을 주는 헤더를 추가하여 해결
app.use((req, res, next) => {
  res.header("Access-Control-Allow-Origin", "*"); // 모든 도메인
  res.header("Access-Control-Allow-Origin", "https://example.com"); // 특정 도메인
});
  • CORS 모듈 사용
const cors = require("cors");
const app = express();

app.use(cors());
  • 특정 도메인 접근 허용
const options = {
  origin: "http://example.com", // 접근 권한을 부여하는 도메인
  credentials: true, // 응답 헤더에 Access-Control-Allow-Credentials 추가
  optionsSuccessStatus: 200, // 응답 상태 200으로 설정
};

app.use(cors(options));
  • 특정 요청 접근 허용
app.get("/example/:id", cors(), function (req, res, next) {
  res.json({ msg: "example" });
});
  • 스프링 CORS : @CrossOrigin 어노테이션 사용하기
    메소드 레벨 및 글로벌 레벨에서 srping mvc 애플리케이션에서 spring cors를 지원하는 방법이다. sprign mvc는 @CorssOrigin 어노테이션을 제공한다. 이 어노테이션은 어노테이션이 달린 메소드 또는 타입을 교차 출처를 허용하는 것으로 표시한다.

기본적으로 @CrossOrigin은 모든 출처, 모든 헤더, @RequestMapping 주석에 지정된 Http 메소드에 최대 30분을 허용한다. 어노테이션에 속성 값을 넣어 기본 값을 대체할 수 있다.

속성값을 살펴보면,

  • origins
    - 허용된 출처, 이 값은 pre-flight 응답과 실제 응답 모두에 access-control-allow-origin헤더에 배치된다.
  • allowedHeaders
    - 실제 요청 중에 사용할 수 있는 요청 헤더 목록이다. pre-flight의 응답 헤더인 access-control-allow-header에 값이 사용된다.
  • allowCredential
    - 브라우저가 요청과 관련된 쿠키를 포함해야 되는지 여부를 결정한다.
    - 이 값이 true이면, pre-flight 응답에는 값이 true로 설정된 access-control-allow-credentials 헤더가 포함된다
@CrossOrigin(origin="*", allowedHeaders = "*")
@Controller
public class MainController {
	@GetMapping(path = "/")
	public String main(Model model) {
		return "main";
	}
}
  • 스프링 CORS : CorsFilter 사용하기
    서블릿 필터 인터페이스를 이용하여 개발되었다. 웹 서버의 모든 리소스의 요청을 가로채서 Cross domain request인지 체크하여 실제 요청 페이지에 전달하기전에 적절한 CORS 정책과 해더들을 적용한다.

  • Access-Control-Allow-Origin
    도메인 간 요청을 할 수 있는 권한이 부여된 도메인을 지정한다.

  • Access-Control-Allow-Credentials
    도메인 간 요청에 credential 권한이 있는지 없는지 지정한다.

  • Access-Control-Expose-Headers
    노출하기에 안전한 헤더를 나타낸다.

  • Access-Control-Max-Age
    pre-flighted 요청이 얼마만큼의 시간동안 캐시되는지

  • Access-Control-Allow-Methods
    리소스에 접근할 때 메소드가 허용되는지

  • Access-Control-Allow-Headers
    어떤 헤더 필드 네임이 실제 요청에서 사용할 수 있는지 가리킨다.

@Component
public class SimpleCorsFilter implements Filter {

    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) res;
        response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin"));
        response.setHeader("Access-Control-Allow-Credentials", "true");
        response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE, PUT, PATCH");
        response.setHeader("Access-Control-Max-Age", "3600");
        response.setHeader("Access-Control-Allow-Headers", "Content-Type, Accept, X-Requested-With, remember-me");
        chain.doFilter(req, res);
    }

    @Override
    public void init(FilterConfig filterConfig) {
    }

    @Override
    public void destroy() {
    }
}
profile
으쌰으쌰🐜🐜

0개의 댓글