CORS

CORS, SOP, Cross-Origin Resource Sharing, Same-Origin-Policy
Sang Un Lee's avatar
Jul 04, 2024
CORS
 
 

1. 배경 개념

1.1 origin

 
  • origin = scheme + host + port
    • scheme : URL의 프로토콜을 나타냄
      • ex. http, https, ftp 등
    • host : 서버의 도메인 이름 or IP 주소
      • ex. localhost, www.naver.com
    • port : 네트워크 포트
      • 표준 포트가 아닐 경우 나타냄
      • 포트가 URL에 없는 경우, origin = scheme + host
      • ex. 8080, 3306
 
  • 예시 : URL: https://localhost:8080/surveys
    • scheme: https
    • host: localhost
    • port: 8080
    • path: /surveys

1.2 same-origin

 
  • 클라이언트가 서버에 요청을 보냈을 때
    • 서버의 origin과 클라이언트의 origin이 동일한 경우
 
  • 예시
    • https://aurora.net/home -> https://aurora.net/api/pos?id=290
    • 클라이언트, 서버 둘 다 origin이 https://aurora.net 으로 동일

1.3 cross-origin

 
  • 클라이언트가 서버에 요청을 보냈을 때
    • 서버의 origin과 클라이언트의 origin이 다른 경우
  • 예시
https://localhost:8080/home -> https://aurora.net/api/pos?id=290 https://localhost:8080/home -> https://127.0.0.1:8080/api/pos?id=290 https://localhost:8080/home -> https://127.0.0.1:3000/api/pos?id=290
  • 클라이언트의 origin : https://localhost:8080
  • 서버의 origin은 이와 다르므로 cross-origin

1.4 Same-origin policy

 
  • 브라우저의 역할
    • 클라이언트
    • ex. 웹 페이지를 로드, 사용자와 상호작용, HTTP 요청을 서버에 보내고 응답을 처리
 
  • 기본적으로 브라우저는 same origin의 HTTP 요청만 허용함
    • Same origin policy(보안 정책)
 
  • SOP가 필요한 이유 : 클라이언트와 서버를 보호하기 위해서
 
  • 서버를 보호하는 관점
    • 클라이언트가 악의적인 스크립트를 요청으로 보내는 것을 제한하기 위함
    • CSRF (Cross-Site Request Forgery)
      • 공격 대상: 주로 서버
      • 공격 방식 : 사용자가 신뢰하는 웹 사이트에 이미 로그인된 상태 → 공격자가 사용자를 속여 특정 요청을 수행
        • 사용자가 의도하지 않은 요청이 서버로 전송돼서 서버에서 의도하지 않은 동작 발생 가능
 
  • 클라이언트를 보호하는 관점
    • 사용자의 민감한 정보(쿠키, 세션 데이터 등)가 악의적인 스크립트에 의해 탈취되는 것을 방지하기 위함
    • XSS (Cross-Site Scripting)
      • 공격 대상 : 주로 클라이언트 (브라우저에서 실행되는 사용자)
      • 공격 방식 : 공격자가 악성 스크립트를 웹 페이지에 삽입 → 사용자가 페이지를 열었을 때 스크립트가 실행
        • 이를 통해 사용자의 세션 정보, 쿠키 등을 탈취 가능

1.5 CORS

 
  • CORS : 브라우저에서 다른 origin으로 요청을 허용하는 메커니즘
 
  • CORS 요청을 할 때 클라이언트, 서버가 해야하는 것
    • 클라이언트 : 없음, 요청을 실행하기만 하면 됨
    • 서버 : 클라이언트의 origin을 허용하고 응답
      • 서버는 Access-Control-Allow-Origin 응답 헤더를 전송
      • 이 응답 헤더의 값은 * 또는 특정 origin이 될 수 있음
        • * : 서버가 모든 origin의 요청을 수락한다는 의미
 
  • CORS 요청에 대한 응답이 왔을 때
    • 클라이언트 : 응답 검토
    • Access-Control-Allow-Origin 에 * 값이 있거나 origin 값이 클라이언트의 origin과 일치하면 브라우저는 응답을 수락함
    • 그렇지 않으면 응답을 삭제하고 콘솔에 CORS 설정 오류를 표시
 
  • CORS 메커니즘 과정
    • 1. 브라우저(origin X)가 서버(origin Y)로 요청을 보냄 2. 서버가 브라우저의 요청에 응답, 이때 Access-Control-Allow-Origin 응답 헤더를 포함하고 origin X를 해당 헤더의 값으로 넣음 3. 브라우저 : 서버 응답에 Access-Control-Allow-Origin 응답 헤더의 값에 origin X가 있는지 확인 4.1 origin X가 있다면 응답을 수락 4.2 origin X가 없다면 응답을 폐기하고 CORS 오류 메세지 표시
 
  • origin을 알 수 없는 경우, Access-Control-Allow-Originnull로 설정할 수도 있음
    • origin을 알 수 없는 경우 예시 : 웹사이트가 아닌 파일에 접근하는 경우

2. Preflight

 
  • 위 내용은 CORS라는 메커니즘을 사용해서 origin이 다른 경우 요청을 처리하는 방법
    • 실제로 CORS 요청을 보내기 전에 브라우저는 preflight 요청을 보냄
    • Preflight 요청의 목적 : 서버가 실제 요청을 허용하는지 확인
 
  • 브라우저가 preflight 요청을 보내는 경우 : 아래 조건 중 하나라도 충족하는 경우
      1. HTTP 메서드가 GET, POST, HEAD가 아닌 경우
          • GET, HEAD, POST가 아닌 경우 데이터 수정/삭제와 관련 있을 수 있으므로 보안상 서버의 허가를 확인
      1. Content-Type 헤더의 값이 아래 3가지 중 하나가 아닌 경우
          • application/x-www-form-urlencoded
          • multipart/form-data
          • text/plain
          • application/x-www-form-urlencoded, multipart/form-data, text/plain은 일반적으로 안전한 MIME 타입으로 간주됨
            • 위 타입 외에 application/json과 같은 Content-Type은 데이터의 구조가 복잡할 수 있으므로 서버가 이를 처리할 준비가 되어 있는지 확인하기 위해 Preflight 요청이 필요
      1. 요청 헤더에 Accept, Accept-Language, Content-Language 외에 다른 요청 헤더가 있는 경우
          • ex. Authorization와 같은 헤더가 있다면 preflight 요청 필요
          • Accept, Accept-Language, Content-Language는 일반적으로 안전한 헤더로 간주됨
            • 그러나 Authorization과 같은 다른 헤더는 인증 정보나 사용자 데이터와 관련될 수 있어 보안 검증이 필요함
            • 따라서 서버가 이러한 헤더를 수락할 준비가 되어 있는지 확인하기 위해 preflight 요청을 보냄
      1. XMLHttpRequest 객체가 업로드 이벤트를 포함하는 경우
          • 파일 업로드와 같은 작업은 더 많은 리소스를 필요로 하고 보안 위험이 있을 수 있음
            • 따라서 서버가 이러한 요청을 허용할 준비가 되어 있는지 확인하기 위해 Preflight 요청을 보냄
 
  • 위 4가지 조건 중 하나라도 해당된다면, 브라우저는 실제 CORS 요청을 보내기 전에 서버에 preflight 요청을 보내서 서버가 해당 요청을 허용하는지 확인
 
  • Preflight 요청 : HTTP 메서드 중 OPTIONS 메서드를 사용
    • 서버가 이에 적절한 응답을 보내면, 브라우저는 그때 실제 요청을 보냄
 
  • Preflight 요청은 CORS 요청을 보낼 때만 preflight 요청을 보냄
    • Preflight 요청을 통해 서버가 CORS 요청에 대한 응답을 잘 보낼 수 있도록
 
  • Preflight 요청에서 브라우저는 아래와 같은 응답 헤더를 보냄
      1. Origin: 클라이언트의 origin
      1. Access-Control-Request-Method: 실제 CORS 요청에서 사용할 HTTP 메서드 (하나의 메서드만 지원)
      1. Access-Control-Request-Headers: 단순하지 않은 헤더들
 
  • 서버는 preflight 요청에서 다음을 확인
    • 요청의 origin(Origin)
    • CORS 요청의 메서드(Access-Control-Request-Method)
    • CORS 요청에 포함된 추가 헤더들(Access-Control-Request-Headers)
 
  • 서버는 이 정보들이 서버의 허용 목록에 있는지 확인함
    • 허용 목록에 있다면 서버는 200번대의 status code로 응답
    • 이 응답에는 응답 본문은 없음
 
  • preflight 요청에 대한 응답으로, 서버는 다음과 같은 응답 헤더를 보냄
      1. Access-Control-Allow-Origin: 허용된 출발지 도메인
      1. Access-Control-Allow-Methods: 허용된 HTTP 메서드들
      1. Access-Control-Allow-Headers: 허용된 요청 헤더들
    • 이는 서버가 브라우저에게 해당 CORS 요청을 허용한다는 의미
    • 이후 브라우저는 해당 서버에게 CORS 요청을 보낼 수 있음
    •  
  • preflight 요청의 예시
    • OPTIONS /api/posts HTTP/1.1 User-Agent: Chrome Host: 127.0.0.1:8080 Accept: */* Origin: http://localhost:3000 Access-Control-Request-Method: GET Access-Control-Request-Headers: Security-Level, App-Name
 
  • preflight 응답 예시
    • HTTP/1.1 204 No Content Access-Control-Allow-Origin: http//localhost:3000 Access-Control-Allow-Methods: GET, DELETE Access-Control-Allow-Headers: Security-Level, App-Name
 
  • Preflight 요청을 포함한 CORS 요청을 처리하는 과정
    • 1. 브라우저(origin X)가 서버(origin Y)로 요청 P를 보냄 - 요청 PHTTP 메서드 M과 헤더 C를 포함 2.1 HTTP 메서드 M이 간단한 메서드 && 헤더 C가 간단한 헤더라면 -> 요청 P를 서버로 보냄 -> 서버 응답() - 간단한 HTTP 메서드 : GET, POST, HEAD - 간단한 헤더 : Accept, Accept-Language, Content-Language, Content-Type(Content-Type은 값이 application/x-www-form-urlencoded, multipart/form-data, text/plain 중 하나인 경우) 2.2 HTTP 메서드 M이 간단하지 않거나 | 헤더 C가 간단하지 않다면 -> preflight 요청 F를 보냄 - 요청 F의 헤더 - origin = X - Access-Control-Request-Method = M - (헤더 C가 간단하지 않은 경우) Access-Control-Request-Headers = C 3. 서버 : X, M, C가 허용 목록에 있는지 확인 4. 허용 목록에 있다면 -> 요청 F에 대한 preflight 응답을 보냄 - status code : 200 범위 - 응답 헤더 : Access-Control-Allow-Origin, Access-Control-Allow-Methods, Access-Control-Allow-Headers 5. 클라이언트가 preflight 응답을 받았다면, 이제 요청 P를 서버로 보냄 -> 서버 응답()
 
  • Preflight 요청에 대한 응답은 캐시 가능
    • 따라서 캐시가 있다면 preflight 요청을 보내지 않아도 됨
    • 캐시의 기준 : origin + path
      • ex. https://www.naver.com/api/data

3. Images

 
  • 다른 origin의 이미지는 처리를 제한
    • 이미지의 src 속성이 클라이언트와 origin이 다른 경우
    • 브라우저는 해당 이미지를 로드, 표시는 가능하지만 조작은 불가
    • 조작 예시 : toBlob, toDataURL, getImageData
 
  • 이미지에 대해 CORS를 활성화하는 방법
    • 즉, origin이 다른 이미지의 조작을 가능하게 하는 방법
    • crossOrigin 속성의 값을 anonymous 또는 user-credentials로 설정
      • anonymous : 자격 증명이 필요하지 않은 경우
      • user-credentials : 자격 증명이 필요한 경우

4. CORS 요청, 응답에 사용되는 헤더

4.1 요청 헤더

 
  • Origin
    • 브라우저에 의해 추가됨
    • 클라이언트의 origin을 표시
    • 여기에 대응되는 응답 헤더 : Access-Control-Allow-Origin
    •  
  • Access-Control-Request-Method
    • 브라우저가 preflight 요청 시 추가함
    • 실제 CORS 요청에서 클라이언트가 사용할 HTTP 메서드를 포함
    • 여기에 대응되는 응답 헤더 : Access-Control-Allow-Methods
 
  • Access-Control-Request-Headers
    • 브라우저가 preflight 요청 시 추가함
    • 실제 CORS 요청에서 클라이언트가 보낼 HTTP 헤더를 포함
    • 여기에 대응되는 응답 헤더 : Access-Control-Allow-Headers
 

4.2 응답 헤더

 
  • Access-Control-Allow-Origin
    • 값이 *일 경우 모든 출처가 허용됨을 의미
    • 특정 값이 있을 경우 서버가 해당 origin에서만 요청을 허용함을 의미
    • 값이 null일 경우 웹 사이트가 아닌 출처(예: 파일)에서의 요청만 허용함을 의미
    • 여기에 대응되는 요청 헤더 : Origin
 
  • Access-Control-Allow-Credentials
    • 값이 true이면 서버가 쿠키와 같은 사용자 자격 증명의 사용을 지원함을 나타냄
 
  • Access-Control-Allow-Methods
    • 서버에서 허용하는 HTTP 메서드들의 목록
    • 간단한 메서드(GET, POST, HEAD)는 포함할 필요는 없지만, 포함하는 것이 좋은 관행
    • 이 헤더는 preflight 요청에 대한 응답으로만 보내야 함
    • 여기에 대응되는 요청 헤더 : Access-Control-Request-Methods
 
  • Access-Control-Allow-Headers
    • 서버에서 허용하는 HTTP 헤더들의 목록
    • 이 헤더는 preflight 요청에 대한 응답으로만 보내야 함
    • 여기에 대응되는 요청 헤더 : Access-Control-Request-Headers
 
  • Access-Control-Max-Age
    • preflight 요청에 대한 응답의 캐시를 유지할 최대 시간
    • 이 헤더는 preflight 요청에 대한 응답으로만 보내야 함
 
  • Access-Control-Expose-Headers
    • 브라우저가 읽을 수 있는 헤더들
    • 선택 사항이며, CORS 요청이 성공적으로 처리되기 위해 반드시 필요하지는 않음
 
Share article

spencer-tech