Jun 개발노트

CORS?..Preflight?

2021-12-10

Who should read this article?

Everyone, really.

0. 왜 작성해야 하는가?

  • 스터디 주제!
  • preflight는 언제 발생하지?
  • 왜 IMG, StyleSheet, Script 요청은 CORS를 발생하지 않는가?

1. Process

  • SOP : same-origin policy
  • CORS: Cross-Origin Resource Sharing
  • preflight
  • How to solve

2. SOP란?

  • 브라우저에서 작동하는 리소스를 처리하는 보안 방식
  • 단, 출처가 다른 것도 허용하는 부분도 있다.
    • <script> <link> <img> <video> <audio> <object> <embed> @font-face, <iframe>
  • Web Storage와 indexDB 같이 브라우저 데이터 저장소 또한 SOP를 적용받는다.
    • Cookie는 SOP 정책에 해당하지 않고, 모든 도메인에 접근하고 설정할 수 있다(그래서 광고 같은데서 쿠키를 활용해서 사용하는것으로 알고 있음) 그래서 쿠키의 정보는 출처가 불분명하므로 안전하지 않다.

3. CORS란?

  • SOP(브라우저 보안방식)에서 발생하는 에러

    • 다른 출처로 부터 요청하는 css, js, img 파일은 정상적으로 작동하지만,
    • script상에서 다른 리소스를 요청하는 것은 CORS에러가 발생한다.
    // localhost:63342/index.html
    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <title>Title</title>
      <link href="http://localhost:5500/style" rel="stylesheet">
    	// color: red
      <script src="http://localhost:5500/js"></script>
    	// console.log('hello Wrold')
    </head>
    <body>
    <h1>Client</h1>
    
    <img src="http://localhost:5500/img/1">
    <script>
      (async () => {
        const result = await fetch('http://localhost:5500/hello')
        console.log(result.json())
      })();
    </script>
    
    </body>
    </html>
    

    cors

    4. Preflight trigger

    모든 Request는 Preflight를 보내는줄 알았는데......... 아니였다...

    • Preflight란? 브라우저(클라이언트)는 직접적으로 리소스를 요청을 하기보다는, OPTIONS 메소드를 통해 해당 서버에서 어떤 Method를 허용하는지, 확인하여 리소스를 요청한다.
    • 몇가지 조건을 만족해야지 preflight를 trigger된다
      • Method : GET / HEAD / POST
      • Headers : Accept / Accept-Language / Content-Language / Content-Type
        • 클라이언트에서 직접적으로 지정할 수 있는 옵션
      • Content-Type 를 지정시(단, 아래 세가지는 발생하지 않는다)
        • application/x-www-form-urlencoded, multipart/form-data, text/plain 제외

    5. How to solve CORS

    백앤드를 찾아가서 CORS 오류 난다고 하면? .... 문제 해결

    • 백앤드 - Response Header에 해당 속성을 넣어주면 된다

      • Access-Control-Allow-Origin를 * 로 하면 모든 request를 열어주는것이므로 보안에 취약하다. 그러므로 서비스하고 있는 client의 url을 넣어주면 gooood
      Access-Control-Allow-Origin : * 
      Access-Control-Allow-Method : GET, POST, PUT. DELETE, OPTIONS (디폴트는 GET, POST)
      Access-Control-Max-age : 3600
      Access-Control-Allow-Headers : Origin, Accept, X-Requestd-With, Content-Type,Access-Contorol-Request-Method,
      Access-Contorol-Request-Header, Authorization
      Access-Contorol-Allow-Credentials
      - 클라이언트 요청이 쿠키를 통해서 자격 증명을 해야 하는 경우에 True를 응답받은 클라이언트는 실제 용청시 서버에서  정의도니 규격의 인증값이 담긴 쿠키를 같이 보내야한다.
      
    • 프론트앤드 - 방법없지만, 잘못된 Header 필드로 보내게 된다면.. CORS가 발생한다.

      • response 부분을 수정하는것이라, 불가능하다.

      • 하지만 개발환경일 경우, proxy를 설정하여, 요청 origin을 서버 주소로 바꿔 요청 할 수 있다.

        // react cra
        // package.json
        {
        "proxy": "http://localhost:4000"
        }
        
        // src/setupProxy.js
        const { createProxyMiddleware } = require('http-proxy-middleware');
        
        module.exports = function(app) {
          app.use(
            '/api',
            createProxyMiddleware({
              target: 'http://localhost:5000',
              changeOrigin: true,
            })
          );
        };
        
      • 잘못된 Header 필드로 보내게 된다면.. CORS가 발생한다.

        const result = await fetch('http://localhost:5000/hello', {
                    headers : {
                        'foo' : 'bar',  // cors Error
        				'Accept-Manguage' : 'en' // cors Error Manguage 오탈자!
                    }
                })