Transfer-Encoding 헤더

  • 안전한 전송을 위해 어떤 인코딩이 메시지에 적용되었는지 수신자에게 알려줌
  • Transfer-Encoding 헤더는 hop-by-hop 헤더로, 리소스 자체가 아닌 두 노드 사이에 메시지를 적용 
  • 다중 노드 연결의 각각의 세그먼트는 Transfer-Encoding의 값을 다르게 사용 가능

    ※ 참고
    • 전체 연결에 있어 데이터를 압축하고자 한다면, Content-Encoding 헤더를 대신 사용하는 것을 추천
    • HEAD 요청에 대한 응답은 GET 요청에 적용될 값을 출력해줌

 

Transfer-Encoding 헤더 지시자

1. chunked

  • Chunked 인코딩 전송방식은 HTTP 1.1에서 사용가능한 스트리밍 데이터 전송 방식
  • 데이터가 일련의 청크(Chunked Message) 내에서 전송
  • Content-Length 헤더는 생략
  • 각 청크의 앞부분에 현재 청크의 길이가 16진수 형태로 오고 그 뒤에는 '\r\n'이 오고 그 다음에 청크 자체가 오며, 그 뒤에는 다시 '\r\n'이 옮
  • 종료 청크는 길이가 0인 것을 제외하면 일반적인 청크와 다르지 않음
  • (비어있을수도 있는) 연속된 엔티티 헤더 필드로 구성된 트레일러(Trailer)가 옮

 

2. compress

  • Lempel-Ziv-Welch (LZW) 알고리즘을 사용하는 형식
  • compress 값의 이름은 알고리즘을 구현한 UNIX compress 프로그램에서 차용된 것
  • compress는 대부분의 UNIX 배포판에서 제외된 압축 프로그램
  • content-encoding에서 compress는 특허 문제로 인해 오늘날 거의 대부분의 브라우저에서 사용되지 않고 있


3. deflate

  • (RFC 1951에 정의된) deflate 압축 알고리즘과 함께 (RFC 1950에서 정의된) zlib 구조체를 사용

 

4. gzip

  • 32비트 CRC를 이용한 Lempel-Ziv coding (LZ77)을 사용하는 형식
  • gzip은 근본적으로 UNIX gzip 프로그램의 형식
  • HTTP/1.1 표준에서 gzip을 content-encoding을 지원하는 서버는 호환성 목적을 위해 x-gzip을 별칭으로 인지할 것을 권고

 

5. identity

  • 압축이나 수정이 없는 정체성 기능을 나타냄 
  • 명시적으로 지정되는 경우를 제외하고 항상 허용 가능한 것으로 간주

 

 

Transfer-Encoding: chunked 헤더 사용 설명

  • Chunked 방식은 전달하려는 컨텐츠의 사이즈가 큰 경우 서버의 처리 지연을 보완할수 있는 방법
  • Transfer-Encoding 헤더는 Chunked 전송방식으로 데이터를 전달하려 할때 주로 사용되는 헤더
  • HTTP 1.1에서는 Content-Length라는 헤더로 전달하고자 하는 컨텐츠의 사이즈 표시 필요
  • 하나의 TCP 커넥션에 여러개의 요청이 가능하기 때문에 클라이언트는 요청마다 전달될 컨텐츠의 데이터 사이즈를 미리 알고 있어야 함 

 

  • Content-Length 헤더로 웹서버에 데이터 처리 과정 → 컨텐츠의 사이즈가 엄청 큰 경우 문제가 발생
    1. 웹서버가 클라이언트의 요청을 받음
    2. 웹서버는 클라이언트에게 전달해 줄 컨텐츠의 사이즈를 먼저 계산 
    3. 계산된 컨텐츠의 전체 사이즈와 함께 데이터를 전달

      ※ 주의
      • 2번 과정에서 컨텐츠의 사이즈가 엄청 큰 경우 문제가 발생 가능
      • 컨텐츠가 큰 경우 전체 사이즈를 계산하는 과정이 오래 걸림 → 클라이언트가 느끼는 지연시간에 반영됨
      • 컨텐츠 사이즈가 큰 경우, 더 효율적인 다른 방식이 필요

 

  • 컨텐츠의 사이즈가 매우 큰 경우 Content-Length 헤더 문제가 발생 가능 → "Chunked" 전송(청크 인코딩; Chunked transfer encoding))
    • Chunked 전송 방식 개념
      • 전체 Chunk를 한번에 주지 않고, Chunked 방식으로 조금씩 조금씩 전송하는 방식
      • 웹서버는 Chunked 방식으로 데이터를 전송하는 경우에는 전체 사이즈를 한번에 계산할 필요가 없음 
      • 보내려는 Chunked만 먼저 클라이언트에게 알리고 데이터를 보냄 → 본문이 동적으로 생성됨에 따라, 서버는 일부를 버퍼에 담은 뒤 한 청크를 크기와 함께 청크 메시지 전송
          
    • Chunked 전송 방식 설명
      • 클라이언트가 이미지를 요청하면, 웹서버는 전체 이미지의 일부분을 전달
      • 전체 이미지의 일부분 사이즈만 알려주면 되기 때문에 전체 사이즈를 계산하지 않아도 됨으로 기존 전송방식의 단점을 극복한 방식
      • 컨텐츠의 마지막 부분을 전송하고 나서는 "0" 을 보내어 데이터가 모두 전송되었음을 알림
      • Chunked 전송방식에는 Content-Length 헤더가 존재하지 않음
         
    • Chunked 방식의 장점
      • 큰 데이터 전송에도 HTTP 연결이 중간에 끊어지지 않게 유지할 수 있음 → HTTP 1.1 연결 유지(Connection: keep-alive, 지속 커넥션)가 활성화
      • Content-Length가 필요 없음 → 크기가 가변적인 데이터 전송에 유리(DB 쿼리 결과를 출력하는 HTML 테이블 / 큰 이미지 등에 사용)
      • 전체 컨텐츠를 생성할 때까지 대기하지 않음
         
    • Chunked 방식을 통해 데이터 전송 과정
      1. Transfer-Encoding: Chunked를 통해 Chunked 전송 방식으로 데이터를 전송할 것을 알림 
      2. Transfer-Encoding: Chunked를 보낸 이후 전송하려는 데이터의 부분적인 사이즈를 16진수로 표시하여 알림
      3. 실제 데이터에 16진수로 표시한 데이터양만큼 보냄 
      4. 위 2~3번 과정을 반복하여 전체 데이터를 전송
      5. 데이터의 마지막 끝부분에 "0" 을 표시하여 데이터 전송이 완료되었음을 알림 
        ### HTTP 응답
        HTTP/1.1 200 OK\r\n
        Content-type: text/plain\r\n
        Transfer-encoding: chunked\r\n
        \r\n
        
        ### 청크 #1
        9\r\n
        HI, Hippo\r\n
        
        ### 청크 #2
        25\r\n
        My name is Hippo. byebye~\r\n
        
        ### 마지막 청크
        0\r\n
        \r\n​
 

 

 

참고 자료 : 분도랑: [HTTP 프로토콜 강좌]#20 HTTP 일반 헤더 II - Transfer-Encoding, Upgrade, Warning, Trailer (withbundo.blogspot.com)

참고 자료 : Transfer-Encoding - HTTP | MDN (mozilla.org)

참고 자료 : 청크 인코딩(Chunked transfer e.. : 네이버블로그 (naver.com)

 

'HTTP > HTTP 헤더' 카테고리의 다른 글

HTTP 프로토콜 Vary 헤더  (0) 2022.06.25
HTTP 프로토콜 server 헤더  (0) 2022.06.25
HTTP 프로토콜 Range 헤더  (0) 2022.06.25
HTTP 프로토콜 Pragma 헤더  (0) 2022.06.25
HTTP 프로토콜 Last-Modified 헤더  (0) 2022.06.25

HSTS란

  • 사이트가 오직 HTTPS 통신만 할 수 있음을 접속하고자 하는 Browser에게 알려 주는 기능
  • 보안을 강화시킬 목적으로 Browser에게 HTTPS만 사용하도록 강제하는 기능
  • Browser에서도 HSTS 기능을 지원해야 HSTS 기능이 제대로 동작

 

  • 2010년 이후에 출시된 대부분의 Browser 버전에서는 HSTS 기능을 지원
  • HSTS (HTTP Strict Transport Security)는 HTTPS를 클라이언트의 브라우저에서 강제화하여 최초 접속 시부터 HTTPS로 접속할 수 있도록 유도하기 위해 만들어짐 → HTTPS를 이용한 웹사이트를 운영 중이라면 HSTS를 필수로 설정하여 운영해야 함
  • SSL Stripping 공격(SSL/TLS Hijacking)을 방지하기 위하여 HSTS 기능을 사용
  • 사용자가 실수로 HTTPS를 지원하는 Site를 HTTP접속 했을 때 중간자 공격에 의해 통신 정보가 공격자에게 노출이 되는 것을 방지하기 위해 강제로 HTTPS로 접속하기 위한 목적으로 사용
  • HSTS는 cookie hijacking을 방지하는 용도로도 사용됨
  • Browser에서 HSTS기능을 지원한다고 해도, SSL stripping 공격에 완전히 안전한 것은 아님

 

  • 공격자가 특정인을 대상으로 표적공격을 한다면 아래와 같은 방법을 취하여 HSTS를 무력화
    1. 특정인의 PC에 침투해서, Browser에 설정된 HSTS기능을 disable시킴
    2. HSTS List에서 특정 사이트에 대한 정보를 초기화

 

 

HSTS 기능의 동작

  1. Browser로 접속 시, 주소창에 “https://” 또는 “http://” 와 같은 프로토콜 이름을 입력하지 않고, 단지 도메인 이름(예를 들면, www.naver.com)만 입력
  2. Browser는 먼저 HTTP(“http://” 사용)로 해당 도메인에 접속을 시도
  3. 도메인 이름만 추출하여, HSTS List와 비교하여 존재하면(도메인 이름에 HSTS가 설정 O), HTTPS을 사용하여 접속
  4. 해당 도메인이 HTTPS만을 지원하는 사이트(HSTS)라면, ”301 Redirect” 또는 ”302 Redirect” response를 보내어 사이트가 HTTPS로 다시 접속하라고 지시
    • Browser가 이전에 접속한 적인 없는 HTTPS 지원 사이트에 HTTP로 접속하면, “301 Redirect” Reply Message로 다시 HTTPS로 접속
    • Server는 HTTPS Reply Message에 "HSTS Policy"를 넣어서 전송 → 암호화 되기 전 packet인, HTTP response header field에 "Strict-Transport-Security" 필드 정보 삽입
    • Browser는 위 정보를 받아서 내부 HSTS List를 구성
    • Server는 "Max Age 값", "Subdomain 적용여부 값", "Preloaded List에 추가여부 값"을 HTTP response header field에 담아 "Strict-Transport-Security" 함께 client에 전달
    • 만약, Web Browser가 HTTP Protocol로 Web Server에 접속하고, HTTP Reply Message에 HSTS Policy 정보가 들어 있는 경우는 무시
  5. 사이트는 status code로 HTTPS로 redirect를 인식한다면, 사용자는 주소창 옆에 있는 자물쇠 아이콘 또는 접속된 URL주소 앞에 “https://”를 보고, 해당 사이트가 HTTPS 지원함을 인식

 

 

HSTS response header의 Syntax

  • HSTS header example
    Strict-Transport-Security: max-age=<expire-time>
    Strict-Transport-Security: max-age=<expire-time>; includeSubDomains
    Strict-Transport-Security: max-age=<expire-time>; preload

 

1. Max Age 값

  • HSTS List 에 존속하는 시간
  • Age 값이 0(zero)인 경우는 HSTS Policy를 삭제하라는 명령

 

2. Subdomain 적용 여부 값

  • subdomain에 HSTS를 적용할지 여부를 알려줌

 

3. Preloaded HSTS Lists

  • 이미 만들어져 있는 HSTS Lists를 Browser에 제공하는 기능도 지원 → "Preloaded HSTS Lists"
  • Preloaded HSTS Lists는 Browser 설치 또는 Update 할 때 Browser Vendor에 의해 제공
  • Preloaded HSTS Lists를 사용하는 목적은 SSL Stripping 공격을 방지하기 위한 목적
  • 사이트를 "Preloaded HSTS Lists"에 포함시키려면 "hstspreload.org" 사이트에 신청 → 요구하는 기능을 충족한 다음에 양식에 맞추어 신청
  • 사이트가 "Preloaded HSTS Lists"에 포함되어 있는 지 확인도 "hstspreload.org" 사이트에서 가능
  • HTTP만 지원하는 사이트를 HSTS List에 추가하거나, 공격자에 의해 HTTP을 지원하는 사이트로 Redirect 된 경우는, "Your connection is not private" 경고 메시지가 출력 → 접속 X
  • DNS Spoofing에 의한 Redirection은 IP 주소 자체가 변경되는 경우는 여기에 해당 X
  • "HSTS Lists""Preloaded HSTS List"가 각각 존재하는 것이 아니고, 개념상 미리 만들어져 있는 "HSTS Lists""Preloaded HSTS Lists"라고 칭함

 

 

HSTS 응답을 받는 웹 사이트에 대해서 https 접속을 강제화

  • max-age가 63072000초로써 rsec.kr은 최초 접속 후 2년 동안 브라우저에서 HTTPS 접속 강제화

 

  • 설정된 HSTS 내역은 클라이언트의 브라우저에서 확인할 수 있는데, 크롬 브라우저의 경우는 아래와 같은 명령어로 확인 가능 (크롬 브라우저 주소창에 입력)
    1. 브라우저 사용자가 직접 특정 사이트를 HSTS List에 추가하는기능 제공
    2. Chrome Browser 인 경우는, 주소창에 “chrome://net-internals/#hsts” 입력하여 설정
    3. 설정된 값을 확인 및 삭제도 가능
    4. HSTS 접속 URL : chrome://net-internals/#hsts

 

  • 위 명령을 통하여 HSTS 를 적용할 도메인을 직접 추가/삭제하거나 쿼리하여 내용을 조회 가능
  • HSTS는 서버 측 응답을 신뢰하여 브라우저에 일정시간 (max-age)동안 등록이 되기도 하지만 google, twitter 등의 웹사이트는 구글 크롬 브라우저에 하드코딩 되어 HSTS가 Preload되어 강제화 되도록 되어 있음
  • 관련 내용은 아래 링크를 통해 조회 가능 (https://www.chromium.org/hsts)
 

HTTP Strict Transport Security

HTTP Strict Transport Security HTTP Strict Transport Security allows a site to request that it always be contacted over HTTPS. HSTS is supported in Google Chrome, Firefox, Safari, Opera, Edge and IE (caniuse.com has a compatibility matrix). The issue that

www.chromium.org

 

 

HSTS (HTTP Strict Transport Security)의 설정 방법

  • HSTS는 웹서버에서 응답해주는 헤더에 최초 포함되어 있는 만큼 웹서버에서 설정 필요
  • Nginx의 경우는 아래와 같이 add_header를 추가한 후 재시작하면 HSTS를 적용 가능
    add_header Strict-Transport-Security "max-age=63072000; includeSubdomains; preload";

 

  • HSTS 설정에 대한 옵션
    1. max-age=63072000
      • HSTS가 브라우저에 설정될 시간이며 초단위로 설정
      • 63072000은 2년을 의미
      • 63072000 = 2 * 365 * 24 * 60 * 60

    2. includeSubdomains
      • HSTS가 적용될 도메인의 subdomain (www.rsec.kr 또는 mail.rsec.kr 따위)까지 HSTS를 확장 적용함을 의미

    3. preload
      • HSTS 적용이 클라이언트 측에서 preload로 이루어짐을 의미

 

 

HSTS (HTTP Strict Transport Security)의 설정 안정성 테스트 방법

  • https://hstspreload.org 웹사이트에서 HSTS 적용이 잘 되어있는지 확인 가능
  • rsec.kr 을 검색한 결과 아래와 같은 결괏값을 추출

 

  • rsec.kr의 인증서가 subdomain까지 지원할 수 없음을 경고하였는데, 이것은 rsec.kr에서 사용하고 있는 Let’s encrypt 인증서가 아직 wildcard 인증서를 지원하고 있지 않아 생긴 문제
  • 향후 wildcard 인증서로 적용하면 해결될 문제 가능
  • https://hstspreload.org 웹사이트 HSTS 확인
 

HSTS Preload List Submission

<!-- We un-hide the form using inline JS so that (when JS is enabled) it shows in the normal rendering order as if it was never hidden. --> Submitting entries to the HSTS preload list via this site requires JavaScript. This form is used to submit domains f

hstspreload.org

 

 

HSTS (HTTP Strict Transport Security)를 이용하여 SSL Strip 방어

  • MITM (Man in the Middle) 공격 방어
  • TLS/SSL로 암호화된 세션은 중간에서 공격자가 그 내용을 감청하더라도 암호화 되어 있기 때문에 데이터가 보호 가능
  • 그렇다면 SSL/TLS 로 암호화 된 세션을 강제로 암호화하지 않은 HTTP 세션으로 유도한다면 어떨까요?
  • 공격자는 중간에서 암호화 되지 않은 데이터를 감청함으로써 그 내용을 알 수 있을 겁니다.
  • 아래의 SSL Strip 공격을 HSTS로 적용하게 되면 클라이언트(브라우저)에서 HTTPS 접속만 강제화 됨으로써 SSL Strip 공격을 방어 가능
  • HSTS는 Sub Domain 을 통한 우회 방법 등 일부 설정이 누락되었을 경우 SSL Strip 공격이 가능

    ※ SSL Strip
    • SSL/TLS로 암호화 된 세션을 강제로 암호화하지 않은 HTTP 세션으로 유도한다면 공격자는 중간에서 암호화되지 않은 데이터를 감청함으로써 내용을 알 수 있음
    • sslstrip이라 불리는 tool을 사용하면, SSL Stripping을 구현 가능
    • sslstrip은 중간자(Man-In-The-Middle) 위치에 있으면서 양쪽(Browser와 Server)을 모두 속이면서 동작
    • HSTS 기능이 없다면, sslstrip은 공격자가 운영하는 Proxy Server 역할

  • SSL Strip 공격하는 과정
    • sslstrip이 중간에서 traffic을 가로챌 수 있도록 하려면, 먼저 ARP poisoning(또는 ARP spoofing) 공격을 수행 → server로 향하는 트래픽을 sslstrip으로 가게 함
    • 사용자는 http://bank 로 연결 시도. 공격자는 그 연결을 그대로 웹서버에 전달
    • 웹서버는 https를 이용하여 연결하도록 응답
    • 공격자는 웹서버의 응답을 조작하여 http로 연결하는 것처럼 사용자에게 응답
    • 사용자는 응답받은 내용을 근거로 http로 연결. 공격자는 내용을 조회한 후 https로 전환하여 웹서버에 연결을 전달
    • 최종적으로 공격자는 ssl 을 strip 하여 중간에서 내용을 감청할 수 있음

 

  • ARP poisoning (ARP spoofing)
    • 동일한 subnet상에서 이루어지는 것
    • ARP poisoning은 ARP cache 값, 즉 IP 주소에 대응되는 MAC 주소 값을 조작하는 공격
    • SSL Strip은 HTTP로 통신하는 모든 내용을 sslstrip.log 파일에 기록해 두는 기능을 제공
    • SSL Strip.log 파일을 통해 Server 접속자의 개인 정보를 추출 가능
    • 실제 환경에서는 공격자가 SSL Strip과 유사한 기능을 가진 악성코드를 설치하여 민감한 정보를 탈취

 

 

  • XMLHttpRequest(XHR) 객체는 서버와 상호작용하기 위하여 사용
  • 전체 페이지의 새로고침 없이도 URL로부터 데이터를 받아올 수 있음 → 전체 페이지와는 독립적으로(비동기적) 데이터를 주고 받을 수 있음
  • 웹 페이지 전체를 다시 로딩하지 않고 일부분만을 갱신 가능
  • XMLHttpRequest 객체는 AJAX(Asynchronos Javascript And XML) 프로그래밍에 주로 사용
  • XMLHttpRequest 객체는 서버로부터 XML 데이터를 전송받아 처리하는 데 사용
  • 요즘 XMLHttpRequest 객체는 XML 뿐 아니라, 모든 종류의 데이터를 받아오는데 사용 가능
  • 현재 대부분의 주요 웹 브라우저는 서버에 데이터를 요청하기 위한 XMLHttpRequest 객체를 내장함
  • HTTP 이외의 프로토콜도 지원 → file과 ftp 포함

XMLHttpRequest 객체의 생성

  • 자바스크립트를 이용하여 XMLHttpRequest 객체를 생성하는 방법
    var xmlHttp = new XMLHttpRequest();

readyState 프로퍼티

  • readyState 프로퍼티는 XMLHttpRequest 객체의 현재 상태를 나타냄
  • readyState 프로퍼티의 값은 객체의 현재 상태에 따라 다음과 같은 주기로 변화
    1. UNSENT (숫자 0) → XMLHttpRequest 객체가 생성됨.
    2. OPENED (숫자 1) → open() 메소드가 성공적으로 실행됨.
    3. HEADERS_RECEIVED (숫자 2) → 모든 요청에 대한 응답이 도착함.
    4. LOADING (숫자 3) → 요청한 데이터를 처리 중임.
    5. DONE (숫자 4) → 요청한 데이터의 처리가 완료되어 응답할 준비가 완료됨.

status 프로퍼티

  • status 프로퍼티는 서버의 문서 상태를 나타냄
    1. 200 → 서버에 문서가 존재함.
    2. 404 → 서버에 문서가 존재하지 않음.

XMLHttpRequest 객체의 전송

  • 자바스크립트를 이용하여 XMLHttpRequest 객체를 전송하는 방법

    var xmlHttp = new XMLHttpRequest();                   // XMLHttpRequest 객체를 생성함.
    xmlHttp.onreadystatechange = function() {           // onreadystatechange 이벤트 핸들러를 작성함.
    
        // 서버상에 문서가 존재하고 요청한 데이터의 처리가 완료되어 응답할 준비가 완료되었을 때
        if(this.status == 200 && this.readyState == this.DONE) {
    
             // 요청한 데이터를 문자열로 반환함.
            document.getElementById("text").innerHTML = xmlHttp.responseText;            
        }
    };
    xmlHttp.open("GET", "/examples/media/xml_httpxmlrequest_data.txt", true);
    xmlHttp.send();
  • XMLHttpRequest를 http를 이용한 예제
    • open이란 메소드를 이용해서 형식, url, 비동기여부(디폴트 : 비동기) 에 원하는 변수를 넣어줌
    • XMLHttpRequest.open(method, url[, async[, user[, password]]]) 형식
      1. 형식 타입 : "GET", "POST", "PUT", "DELETE"
      2. url : 요청 URL
      3. 비동기 여부 : true(비동기), false(동기), defalut(true)
      4. user : 암호화를 사용하는 경우 사용 default null
      5. password : 암호화를 사용하는 경우 사용 default null
        var xmlHttp = new XMLHttpRequest();
        xmlHttp.open("GET", "/examples/media/xml_httpxmlrequest_data.txt", true);
        xmlHttp.send();

responseText 프로퍼티

  • responseText 프로퍼티는 서버에 요청하여 응답으로 받은 데이터를 문자열로 저장
    document.getElementById("text").innerHTML = xmlHttp.responseText;

responseXML 프로퍼티

  • responseXML 프로퍼티는 서버에 요청하여 응답으로 받은 데이터를 XML DOM 객체로 저장

    xmlObj = xmlHttp.responseXML;                                    // 요청한 데이터를 XML DOM 객체로 반환함.
    nameList = xmlObj.getElementsByTagName("name");     // XML DOM 객체에서 요소이름이 "name"인 요소들을 선택함.
    result = "";
    
    for (idx = 0; idx < nameList.length; idx++) {
        // id가 "name"인 요소들의 텍스트 노드를 찾아 그 값을 반환함.
        result += nameList[idx].childNodes[0].nodeValue + "<br>";
    }
    
    document.getElementById("text").innerHTML = result;
  • 위의 예제에서 사용된 programming_languages.xml 파일의 코드 예시
    <?xml version="1.0" encoding="UTF-8"?>
    <programming_languages>
        <language>
            <name>HTML</name>
            <category>web</category>
            <developer>W3C</developer>
            <version status="working draft">5.1</version>
            <priority rating="1">high</priority>
        </language>
        <language>
            <name>CSS</name>
            <category>web</category>
            <developer>W3C</developer>
            <version status="stable">3.0</version>
            <priority rating="3">middle</priority>
        </language>
        <language>
            <name korean="자바">Java</name>
            <category>application</category>
            <developer>Oracle</developer>
            <version status="stable">8.91</version>
            <priority rating="2">high</priority>
        </language>
        <language>
            <name korean="파이썬">Python</name>
            <category>application</category>
            <developer>Python</developer>
            <version status="stable">3.52</version>
            <priority rating="4">middle</priority>
        </language>
    </programming_languages>

비동기식(asynchronous) 요청

  • 서버에 비동기식 요청을 보내기 위해서는 open() 메소드의 세 번째 인수로 true를 전달
  • 비동기식으로 요청을 보내면 자바스크립트는 서버로부터 응답을 기다리면서 동시에 다른 일을 할 수 있음
    xmlHttp.open("GET", "/media/programming_languages.xml", true);

참고 URL : http://www.tcpschool.com/xml/xml_dom_xmlHttpRequest


  • 데이터를 제공하는 파일 서버를 구축해야 하는 경우 AWS의 S3와 CloudFront 서비스를 이용하면 손쉽게 구축 가능 → 이미지의 경우 자주 사용
  • AWS S3와 CloudFront를 통해 Webapp을 만들 경우 CORS관련 문제가 발생 가능
  • 브라우저의 기본 기능을 통해 파일을 직접 다운로드를 하거나 DOM을 이용해 ( 테그나 CSS) 이미지를 표시하기 때문에 문제 발생 X
  • XHR을 통해 데이터를 받아서 사용하는 경우에는 Webapp이 제공되는 도메인과 CloudFront 서비스의 도메인이 다르기 때문에 CORS 문제 발생
  • AWS S3에서 CORS 관련 설정을 제공하지만, 브라우저와 CloudFront의 캐시 기능과 엮이면서 S3의 CORS 기능이 재대로 동작하지 않는 경우가 빈번
  • Unity3d를 이용해서 Facebook Canvas 웹게임을 만들 때 CORS 문제가 발생
  • Unity3d의 기본 HTTP Client인 UnityWebRequest를 이용해서 데이터를 받아오도록 짜면 Facebook Canvas 빌드의 경우 XHR을 사용하도록 컴파일
  • Facebook을 통해 서비스가 되는데, 받아와야 하는 데이터는 AWS S3에 있기 때문에 CORS 문제를 피할 수 없게됨
  • Unity3d쪽은 가능하면 수정하고 싶지 않아 서버쪽에서 CORS 지원하도록 수정

크롬 브라우저 캐시로 인한 CORS 에러

  • 크롬 브라우저의 캐시 정책으로 발생한 문제
  • 광고 이미지를 Facebook Page에 게시물로 올리고, Canvas 게임 안에서도 배너로 표시
  • 유저가 Facebook Page에 접근해서 이미지를 본 후 링크를 눌러 게임에 진입했을 경우에, Unity 게임 안에서 동일한 이미지를 XHR로 받으려고 하면 CORS 에러가 발생
  • 에러 발생과정
    1. 유저가 Facebook Page에서 이미지를 봄 → 이미지는 img 태그로 표시되기 때문에 HTTP 통신을 보면 이미지 요청과 응답에는 CORS 관련 헤더가 없음 (크롬 브라우저는 요청과 응답을 캐시함)
    2. Unity가 XHR로 같은 이미지를 요청 → 요청에는 CORS 관련 헤더를 포함됨
    3. 크롬 브라우저의 네트워크 담당 부분에서 1에서 캐시한 응답 결과 리턴 → 자바스크립트에서 응답된 결과에 CORS 관련 헤더가 없기 때문에 에러가 발생함. (캐싱된 이미지는 CORS관련 헤더가 없음)
  • 2의 요청과 1의 요청은 헤더가 다르고 결과도 다른 경우인데, 크롬 브라우저가 같은 결과가 나올 것을 기대하고 캐시를 사용하는 것이 문제의 원인

  • 매우 마이너한 경우이고, 브라우저의 동작을 명확히 규정하는 표준도 없기 때문에 발생한 경우임

  • 크롬 브라우저가 2의 요청에 1에서 캐시한 결과를 사용하지 않도록 Vary Header를 사용하면 문제 해결됨

  • CloudFront에 Lambda Edge를 추가해서 응답 결과에 아래와 같이 Vary header를 추가

    'use strict';
    // If the response lacks a Vary: header, fix it in a CloudFront Origin Response trigger.
    
    exports.handler = (event, context, callback) => {
        const response = event.Records[0].cf.response;
        const headers = response.headers;
    
        if (!headers['vary'])
        {
            headers['vary'] = [
                { key: 'Vary', value: 'Access-Control-Request-Headers' },
                { key: 'Vary', value: 'Access-Control-Request-Method' },
                { key: 'Vary', value: 'Origin' },
            ];
        }
        callback(null, response);
    };



CloudFront Origin별 캐시 오동작

  • CloudFront가 CORS 요청에 비CORS 응답을 캐시해서 발생

  • S3는 CORS 관련 헤더가 요청에 있을 경우에만 응답에 CORS 관련 헤더를 추가해줌

  • CloudFront도 단순 URL 뿐만 아니라 HTTP 헤더에 따라 다른 결과를 캐시 필요 → Origin 헤더에 따라 다른 결과를 캐시하는 기능을 제공

  • 처음에는 잘 동작하는 듯 했는데, 한 시간 정도 지나면 CORS 요청에 비CORS 응답이 캐시되는 경우가 빈번히 발생

  • 프로필 이미지나 정적 데이터를 제공할 뿐인 단순한 경우라서 CloudFront의 모든 응답에 CORS관련 헤더를 강제로 추가

    'use strict';
    // If the response lacks a Vary: header, fix it in a CloudFront Origin Response trigger.
    // If the response lacks CORS header, ...
    
    exports.handler = (event, context, callback) => {
        const response = event.Records[0].cf.response;
        const headers = response.headers;
    
        if (!headers['vary'])
        {
            headers['vary'] = [
                { key: 'Vary', value: 'Access-Control-Request-Headers' },
                { key: 'Vary', value: 'Access-Control-Request-Method' },
                { key: 'Vary', value: 'Origin' },
            ];
        }
        if (!headers['access-control-allow-origin'])
        {
            headers['access-control-allow-origin'] = [
                { key: 'Access-Control-Allow-Origin', value: '*' }
            ];
        }
        if (!headers['access-control-allow-methods'])
        {
            headers['access-control-allow-methods'] = [
                { key: 'Access-Control-Allow-Methods', value: 'GET' },
                { key: 'Access-Control-Allow-Methods', value: 'HEAD' }
            ];
        }
        if (!headers['access-control-expose-headers'])
        {
            headers['access-control-expose-headers'] = [
                { key: 'Access-Control-Expose-Headers', value: 'ETag' },
                { key: 'Access-Control-Expose-Headers', value: 'Last-Modified' }
            ];
        }
        callback(null, response);
    };

참고 URL : https://medium.com/bgpworks/aws-s3-cloudfront%EB%A1%9C-cdn-%EA%B5%AC%EC%B6%95-%EC%8B%9C-cors-%EA%B0%95%EC%A0%9C-%EC%84%A4%EC%A0%95-eb271a3ca3c6


'HTTP > CORS' 카테고리의 다른 글

XHR (XML Http Request)이란  (0) 2022.07.24
3장. CORS의 HTTP 응답 헤더와 HTTP 요청 헤더  (0) 2022.07.24
2장. CDN을 통한 CORS 및 CORS 요청  (0) 2022.07.24
1장. CORS 기본 정리  (0) 2022.07.24

HTTP 응답 헤더

  • Cross-Origin 리소스 공유 명세에 정의된 대로 서버가 접근 제어 요청을 위해 보내는 HTTP 응답 헤더가 나열

1. Access-Control-Allow-Origin

  • 리턴된 리소스에는 다음 구문과 함께 하나의 Access-Control-Allow-Origin 헤더가 있을 수 있음

    # 특정 Origin 
    Access-Control-Allow-Origin: <origin>
    
    # 모든 Origin 표현을 위해 * (asterisk 사용)
    Access-Control-Allow-Origin: *
  • Access-Control-Allow-Origin은 단일 출처를 지정하여 브라우저가 해당 출처가 리소스에 접근하도록 허용
  • 자격 증명이 없는 요청의 경우, 와일드 카드(*)는 브라우저의 origin에 상관없이 모든 리소스에 접근하도록 허용
  • 예를들어 https://mozilla.org 의 코드가 리소스에 접근 할 수 있도록 하려면 아래와 같이 지정 가능
    Access-Control-Allow-Origin: https://mozilla.org
  • 서버가 와일드 카드(*) 대신에 하나의 origin을 지정하는 경우, 서버는 Vary 응답 헤더에 Origin을 포함해야 함
  • Origin은 화이트 리스트의 일부로 요청 Orgin에 따라 동적으로 변경될 수 있음
  • 서버 응답이 Origin 요청 헤더에 따라 다르다는것을 클라이언트에 알려줌

2. Access-Control-Expose-Headers

  • Access-Control-Expose-Headers 헤더를 사용하면 브라우저가 접근할 수 있는 헤더를 서버의 화이트리스트에 추가 가능
    Access-Control-Expose-Headers: <header-name>[, <header-name>]*
  • 예시) X-My-Custom-Header와 X-Another-Custom-Header 헤더가 브라우저에 전달
    Access-Control-Expose-Headers: X-My-Custom-Header, X-Another-Custom-Header

3. Access-Control-Max-Age

  • Access-Control-Max-Age 헤더는 preflight request 요청 결과를 캐시할 수 있는 시간
  • delta-seconds 파라미터는 결과를 캐시할 수 있는 시간(초)를 나타냄
    Access-Control-Max-Age: <delta-seconds>
  • 예시) preflight request 요청 결과를 1일 캐시
    Access-Control-Max-Age: 86400

4. Access-Control-Allow-Credentials

  • Access-Control-Allow-Credentials 헤더는 credentials 플래그가 true일 때 요청에 대한 응답을 표시할 수 있는지를 나타냄
  • preflight request에 대한 응답의 일부로 사용하는 경우, credentials을 사용하여 실제 요청을 수행할 수 있는지 나타냄
  • simple GET requests는 preflighted 되지 않으므로 credentials이 있는 리소스를 요청하면 반환되지 않음
  • Access-Control-Allow-Credentials 헤더가 없으면 브라우저에서 응답을 무시하고 웹 컨텐츠로 반환되지 않음
    Access-Control-Allow-Credentials: true

5. Access-Control-Allow-Methods

  • Access-Control-Allow-Methods 헤더는 리소스에 접근할 때 허용되는 메서드를 지정
  • Access-Control-Allow-Methods 헤더는 preflight request에 대한 응답으로 사용
    Access-Control-Allow-Credentials: true

6. Access-Control-Allow-Headers

  • preflight request에 대한 응답으로 Access-Control-Allow-Headers 헤더가 사용
  • 실제 요청시 사용할 수 있는 HTTP 헤더를 나타냄
    Access-Control-Allow-Headers: <header-name>[, <header-name>]*


HTTP 요청 헤더

  • cross-origin 공유 기능을 사용하기 위해 클라이언트가 HTTP 요청을 발행할 때 사용할 수 있는 헤더가 나열됨
  • HTTP 요청 헤더는 서버를 호출할 때 설정
  • cross-site XMLHttpRequest 기능을 사용하는 개발자는 프로그래밍 방식으로 cross-origin 공유 요청 헤더를 설정할 필요가 없음

1. Origin

  • Origin 헤더는 cross-site 접근 요청 또는 preflight request의 출처를 나타냄
  • Origin 은 요청이 시작된 서버를 나타내는 URI → 경로 정보는 포함하지 않고, 오직 서버 이름만 포함
  • Origin 값은 null 또는 URI 가 올 수 있음
    Origin: <origin>

2. Access-Control-Request-Method

  • Access-Control-Request-Method 헤더는 실제 요청에서 어떤 HTTP 메서드를 사용할지 서버에게 알려주기 위해, preflight request 할 때에 사용
    Access-Control-Request-Method: <method>

3. Access-Control-Request-Headers

  • Access-Control-Request-Headers 헤더는 실제 요청에서 어떤 HTTP 헤더를 사용할지 서버에게 알려주기 위해, preflight request 할 때에 사용
    Access-Control-Request-Headers: <field-name>[, <field-name>]*

'HTTP > CORS' 카테고리의 다른 글

XHR (XML Http Request)이란  (0) 2022.07.24
4장. AWS S3 + CloudFront로 CDN 구축 시 CORS 강제 설정  (0) 2022.07.24
2장. CDN을 통한 CORS 및 CORS 요청  (0) 2022.07.24
1장. CORS 기본 정리  (0) 2022.07.24
  • CORS(Cross Origin Resource Sharing)는 주로 다른 원본의 컨텐츠에 대한 액세스 권한을 유효성 검증하기 위해 브라우저에서 사용하는 메커니즘

CORS의 개념

  • 브라우저가 웹 페이지를 로드할 때 CORS(동일 출처 정책)을 적용 → 웹 페이지와 동일한 원본에서만 컨텐츠를 패치하도록 허용
  • 경우에 따라서는 웹 페이지에서 해당 웹 사이트를 신뢰하는 여러 원본의 자산에 대한 액세스가 필요 → CORS를 사용
  • Chrome, Firefox 및 Safari 등의 거의 모든 현대 브라우저는 CORS 구현 → CORS와 관련한 원본이 CDN 원본과 동일할 필요 X
  • CORS의 원본은 URI 스킴, 도메인 및 가능한 임의의 포트 번호에 의해 정의
  • CDN은 브라우저 관점에서 CORS 원본으로 간주될 수 있음


CDN에 대한 CORS 설정

  • CDN은 원본의 CORS 설정에 대해 투명함으로 특정 CDN 구성이 필요 X
  • CDN edge 서버가 일부 컨텐츠의 첫 번째 요청에 대해 캐시된 응답을 찾지 못하는 경우 해당 요청을 원본 호스트에 전달 → 원본 서버가 CORS 요청을 처리하도록 설정
  • 요청에 Origin 헤더가 있는 경우 Access-Control-Allow-Origin의 CORS 헤더 및 연관된 값을 사용하여 에지에 다시 응답
  • 해당 헤더 및 값을 포함하는 전체 응답이 CDN에서 캐시
  • 동일한 URI 경로에 있는 오브젝트에 대한 후속 요청이 캐시에서 수행 → 원래 원본에서 수신된 Access-Control-Allow-Origin 헤더 값이 포함

다중 CORS 원본 지원

  • 경우에 따라 특정 원본(전부는 아님)목록이 CDN 컨텐츠에 액세스하도록 허용 → CDN에서 다른 원본의 다른 Access-Control-Allow-Origin 응답 헤더를 제공 필요
  • 원본의 와일드카드 * 아님
  • CDN은 컨텐츠와 함께 헤더를 캐시하므로 일치하지 않을 수 있는 요청에 캐시된 Access-Control-Allow-Origin 헤더를 제공
  • 캐시 키 조회 최적화를 활용하여 다른 원본의 URL에서 다른 매개변수를 추가 가능 → 컨텐츠 및 헤더가 다르게 캐시
  • CDN은 백엔드 서버에서 제공하는 다른 Access-Control-Allow-Origin 헤더를 리턴
  • abc.com 원본의 요청은 access-control-allow-origin: https://abc.com을 리턴
    $ curl -H "Origin: https://abc.com" -H "Referer: https://abc.com/" -i https://cdn.example.com/test.json?domain=abc.com
    HTTP/2 200
    access-control-allow-origin: https://abc.com
    access-control-allow-methods: GET
    access-control-allow-credentials: true
    ### 생략 ###
  • 123.com 원본의 요청은 access-control-allow-origin: https://123.com을 리턴
    $ curl -H "Origin: https://123.com" -H "Referer: https://123.com/" -i https://cdn.example.com/test.json?domain=123.com
    HTTP/2 200
    access-control-allow-origin: https://123.com
    access-control-allow-methods: GET
    access-control-allow-credentials: true
    ### 생략 ###


Origin(Request) 헤더에 따른 CORS 설정

  • Client에서 Request 헤더에 Origin 헤더를 요청 하는데, 해당 헤더에 맞추어 CORS 헤더를 응답받기를 원함
    ## Origin 헤더가  ' *.hippo.com' 인 경우
    ## aaa.hippo.com이면 CORS 헤더를 aaa.hippo.com 으로 응답
    ## bbb.hippo.com이면 CORS 헤더를 bbb.hippo.com 으로 응답
  • Nginx 설정
    ###Location Block
          if ($http_origin ~ hippo.com) {
          more_set_headers "Access-Control-Allow-Origin  $http_origin";
    }
  • 응답 헤더 확인
    $ curl -D- -o/dev/null -H'host:hippo.cdn.co.kr' 127.0.0.1/sample.txt -H 'Origin:https://aaa.hippo.com'
    Server: nginx
    Date: Tue, 25 Feb 2020 04:53:45 GMT
    Content-Type: text/plain; charset=utf-8
    Content-Length: 1641
    Connection: keep-alive
    Last-Modified: Thu, 06 Feb 2020 03:24:17 GMT
    ETag: "1"
    Access-Control-Allow-Origin: https://aaa.hippo.com


CORS 등장 배경

  • 과거 웹사이트를 만들 때, 대부분 하나의 서버에서 브라우저의 모든 요청을 처리
  • 점점 웹사이트가 발전하여서 웹사이트에서 할수있는 일이 많아졌음(ex 날씨 api 를 이용하여 기능을 넣을 필요가 있는 경우)
  • 웹브라우저와 날씨 api 도메인이 서로 달라서 요청을 주고받을 수 없음 → 서로 도메인이 달라 과거에는 해당 문제를 해결하기 위한 JSONP 방식 사용
  • JSONP 방식은 <script> 요소를 사용하여 외부 출처로부터 조회된 내용을 실행하는 것이 허용 → 조회된 내용을 서버에 데이터로 반환하는 용도로 사용
  • JSONP 방식 예제 → CORS가 나오기 전까지 사용 되던 방식으로 보안상 이슈로 사용 X

    # 함수이름을 넣어 요청
    const script = document.createElement("script")
    script.src = "ingg.com/test.json?callback=parseResponse"
    
    # 서버에서는 함수이름을 넣고 매개변수로 데이터를 넣어서 반환
    parseResponse({
    "id" : "123",
    "name" : "ingg",
    })
    
    function parseResponse(data){
      // ...
    }

CORS란

  • CORS는 다른 출처의 리소스가 필요한 경우, SOP를 우회하기 위한 여러가지 방법 중 가장 권장되는 방법
  • 교차 출처 리소스 공유(Cross-Origin Resource Sharing, CORS)는 추가 HTTP 헤더를 사용하여, 한 출처에서 실행 중인 웹 애플리케이션이 다른 출처의 선택한 자원에 접근할 수 있는 권한을 부여하도록 브라우저에 알려주는 체제이다. - MDN


SOP 정책

SOP 정책이란

  • Same Origin(동일 출처 정책)은 scheme, host, port가 모두 같을 때를 의미
  • SOP(Same-Origin Policy)란 같은 Origin에서만 리소스를 공유할 수 있다는 규칙
  • Same Origin Policy(동일 출처 정책)은 어떤 출처에서 불러온 문서나 스크립트가 다른 출처에서 가져온 리소스와 상호작용하는 것을 제한하는 중요한 보안 방식
  • Same Origin(동일 출처 정책)은 잠재적으로 해로울 수 있는 문서를 분리함으로써 공격받을 수 있는 경로를 줄여줌
  • 브라우저에서 다른 서버에 요청할 경우 SOP 정책 영향을 받음
  • 브라우저를 거치지 않고 서버 간 통신을 할 때는 SOP 정책이 적용 X

SOP 정책이 존재이유

  • 다른 출처의 어플리케이션이 서로 통신하는 것에 대해 제약
  • 악의를 가진 사용자가 소스 코드를 보고 CSRF(Cross-Site Request Forgery) 나 XSS(Cross-Site Scripting)와 같은 방법을 사용하여 정보를 탈취를 하는 것을 막음


CORS 동작 방식

  • CORS 접근 제어 시나리오 (교차 출처 리소스 공유가 동작하는 방식)에는 크게 세가지
    1. 프리플라이트 요청 (Preflight Request)
    2. 단순 요청(Simple Request)
    3. 인증정보 포함 요청(Credentialed Request)

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

  • Preflight Request는 요청을 예비 요청과 본 요청으로 나눔
  • OPTIONS 메서드를 통해 다른 도메인의 리소스에 요청이 가능한지 (실제 요청이 전송하기에 안전한지) 확인 작업을 하고, 요청이 가능하다면 실제 요청을 보냄
  • Cross-origin 요청은 유저 데이터에 영향을 줄 수 있기 때문에 Preflight 요청
  • Preflight Request
    • OPTIONS 요청과 함께 두 개의 다른 요청 헤더가 전송
    • Preflight Request가 완료되면 실제 요청을 전송
    • 첫 번째 행은 실제 요청을 전송할 때 POST 메서드로 전송된다는 것을 서버에 알려줌
    • 두 번째 행은 실제 요청을 전송 할 때 X-PINGOTHER 와 Content-Type 사용자 정의 헤더와 함께 전송된다는 것을 서버에 알려줌
      Access-Control-Request-Method: POST                                     # 실제요청의 메서드
      Access-Control-Request-Headers: X-PINGOTHER, Content-Type  # 실제요청의 추가헤더
  • Preflight Response
    • 서버가 메서드와 헤더를 받을 수 있음을 알려줌
    • 첫 번째 행은 응답할 때 서버측 허가되는 서버 도메인(http://foo.example)를 클라이언트에게 알려줌
    • 두 번째 행은 응답할 때 서버측 허가되는 POST, GET, OPTIONS 메서드를 클라이언트에게 알려줌
    • 세 번째 행은 응답할 때 서버측 허가되는 X-PINGOTHER 와 Content-Type 사용자 정의 헤더를 클라이언트에게 알려줌
    • 마지막 행 은 preflight request에 대한 응답을 캐시할 수 있는 시간(초)를 클라이언트에게 알려줌 → 86400초 (=24시간)
    • Preflight를 보내면 사전, 실제 요청 두번이 매번 왔다갔다 하므로 브라우저가 캐싱을 해두고 똑같은 요청을 보낼 때에는, 사전 요청을 보내지 않고 바로 본 요청을 보냄
      Access-Control-Allow-Origin: http://foo.example                    # 서버측 허가출처
      Access-Control-Allow-Methods: POST, GET, OPTIONS              # 허가 메서드
      Access-Control-Allow-Headers: X-PINGOTHER, Content-Type   # 서버측 허가헤더
      Access-Control-Max-Age: 86400                                           # Prefilght 응답 캐시기간

2. 단순 요청(Simple Request)

  • Simple Request는 Preflight Request와 다르게 요청을 보내면서 즉시 cross origin인지 확인
  • Simple Request는 아래 조건을 모두 충족해야함
    1. 메서드는 GET POST HEAD 중 하나
    2. 헤더는 Accept, Accept-Language, Content-Language, Content-Type만 허용
    3. Content-Type 헤더는 다음의 값들만 허용
      • application/x-www-form-urlencoded
      • multipart/form-data
      • text/plain


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

  • 인증 관련 헤더를 포함할 때 사용하는 요청
  • 브라우저가 제공하는 비동기 리소스 요청 API인 XMLHttpRequest 객체나 fetch API는 별도의 옵션 없이 브라우저의 쿠키 정보나 인증과 관련된 헤더를 기본적으로 요청에 담지 않으므로, credentials 옵션을 변경하지 않고서는 cookie를 주고 받을 수 없음
  • 인증정보 포함 요청의 세가지 옵션
    1. omit → 절대로 cookie들을 전송하거나 받지 않음
    2. same-origin → 동일 출처(same origin)이라면, user credentials (cookies, basic http auth 등..)을 전송 (default 값)
    3. include → cross-origin 호출이라 할지라도 언제나 user credentials (cookies, basic http auth 등..)을 전송
  • credentials 설정을 include/true로 설정하면 CORS정책에 의해 Access-Control-Allow-Origin을 모든 출처를 허용하는 * 로 지정할 수 없다는 에러가 발생
  • CORS 설정에서 *을 입력하여 모든 출처를 허용한 경우에는 특정 출처를 정확히 명시
  • 인증 정보 포함하여 요청하는 예시
    fetch('주소', {
    credentials: 'include', // 모든 요청에 인증 정보 포함
    });
  • axios 로 통신할 시, withCredentials 설정을 true 로 넣어주면 됨

    axios.post(주소, 데이터, { withCredentials: true });
    
    // 또는 공통으로 추가
    axios.defaults.withCredentials = true;



CORS 해결 방법

  • CORS 정책 위반으로 에러가 발생했을때 해결하는 방법

1. Access-Control-Allow-Origin 응답 헤더 세팅

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

2. CORS 모듈 사용

  • 아무 옵션없이 설정하면 모든 cross-origin 요청에 대해 응답 → 특정 도메인이나 특정 요청에만 응답하게 옵션을 설정하는 것이 좋음

    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" });
    });

3. webpack-dev-server proxy 기능

  • 리액트 개발환경에서, 서버쪽 코드를 수정하지 않고 해결 가능

  • 아래와 같이 프록시 속성을 설정하면, 서버에서 해당 요청을 받아줌

  • 중간의 프록시 서버 덕분에, domain.com 서버에서는 같은 도메인(domain.com)에서 온 요청으로 인식하여 CORS 에러가 발생 X

    // 프록시 쓰지 않았을때
    // localhost:8080(클라이언트 측) --X (CORS)--> domain.com (서버 측)
    
    // 프록시를 설정 후
    // localhost:8080(클라이언트 측) --O 프록시가 설정된 Webpack Dev Server--> domain.com (서버 측)
    
    module.exports = {
      devServer: {
        proxy: {
          "/api": {
            target: "domain.com",
            changeOrigin: true,
          },
        },
      },
    };

4. package.json에 proxy 값을 설정

  • create-react-app으로 생성한 프로젝트에서는, package.json에 proxy 값을 설정하여 proxy 기능을 활성화하는 방법
    {
      //...
      "proxy": "http://localhost:4000"
    }

Content-Length: 엔터티의 길이

  • Content-Length 헤더는 메시지의 엔터티 본문의 크기를 바이트 단위
  • 어떻게 인코딩 되었든 상관없이 크기를 표현
  • Content-Length 헤더는 메시지를 청크 인코딩으로 전송하지 않는 이상, 엔터티 본문을 포함한 메시지에서는 필수적으로 있어야 함
  • 서버 충돌로 인한 메시지 잘림 검출과 지속 커넥션을 공유하는 메시지를 분할하고자 할 때 필요

1. 잘림 검출

  • 오래된 버전의 HTTP는 커넥션이 닫힌 것을 보고 메시지가 끝났음을 인지
  • Content-Length가 없다면 커넥션이 정상적으로 닫힌 것인지 구분하지 못함
  • 메시지 잘림은 캐싱 프록시 서버에서 특히 취약
  • 캐시가 잘린 메시지를 수신했으나 잘렸다는 것을 인삭하지 못했다면, 캐시는 결함이 있는 컨텐츠를 저장하고 계속해서 제공.
  • 결함있는 컨텐츠를 제공하는 것을 방지하기 위하여, 캐싱 프록시 서버는 명시적으로 Content-Length 헤더를 갖고 있지 않은 HTTP 본문은 캐시하지 않음

2. 잘못된 Content-Length

  • Content-Length가 잘못된 값을 담고 있을 경우 아예 빠진 것보다도 큰 피해를 유발함
  • HTTP/1.1에서는 사용자 에이전트가 잘못된 길이를 받고 이를 인지했을 때, 사용자에게 알려주게 됨

3. Content-Length와 지속커넥션

  • Content-Length는 지속 커넥션을 위해 필수
  • 클라이언트에게 메시지 하나가 어디서 끝나고, 다음 시작은 어디인지 알려줌
  • 커넥션은 지속적이기에, 클라이언트가 커넥션이 닫힌 위치를 근거로 메시지의 끝을 인식하는 것은 불가능
  • HTTP 애플리케이션은 엔터티 본문의 길이와 끝을 인식하고자 Content-Length 헤더를 활용
  • 청크 인코딩을 사용하는 경우는 예외
  • 청크 인코딩은 데이터를 특정 크기를 갖는 청크들로 쪼개어 보내며, Content-Legnth 헤더를 필요로 하지 않음


컨텐츠 인코딩

  • HTTP는 보안을 강화하거나 압축을 통해 공간을 절약할 수 있도록, 엔터티 본문을 인코딩할 수 있게 해줌
  • 만약 본문의 컨텐츠가 인코딩되어 있다면, Content-Length 헤더는 원본이 아닌 인코딩된 본문을 바이트 단위로 정의함
  • HTTP/1.1 명세에서 어떤 헤더도 인코딩 전 원본의 길이를 보내고자 사용되지 않음
  • 클라이언트가 수행한 디코딩 과정의 검증을 어려움
  • Content-MD5 헤더도 인코딩된 문서의 MD5를 담음 → 자주 사용 X

+ Recent posts