본문 바로가기
독서&그 외

'그림으로 배우는 HTTP & Network Basic' 4장~6장

by winteringg 2022. 10. 5.
 

HTTP & Network Basic - 교보문고

재미있게 배워보는 웹과 네트워크 입문 | 이 책은 웹의 근간을 이루는 HTTP를 중심으로 하여 웹, 인터넷 데이터 통신 분야의 기초가 되는 내용들을 다루고 있습니다. 관련 분야를 배우고자 하는

www.kyobobook.co.kr


[제4장, 결과를 전달하는 HTTP 상태 코드]

HTTP 상태 코드

  클래스 설명
1xx Informational 리퀘스트를 받아들여 처리
2xx Success 리퀘스트를 정상적으로 처리
3xx Redirection 리퀘스트를 완료하기 위해서 추가 동작이 필요
4xx Client Error 서버는 리퀘스트 이해 불가
5xx Server Error 서버는 리퀘스트 처리 실패

클라이언트가 보낸 리퀘스트를 서버에서 처리한 결과를 알려준다. 결과가 정상적인지 에러였는지 알 수 있다. 첫 번째 자리는 리스폰스 클래스를 의미하고, 나머지2자리는 분류가 없다. 클래스의 정의만 지킨다면 RFC2616에서 정의된 상태코드를 변경하거나 서버 독자의 상태코드를 만들어도 된다.

 하지만 상태코드는 현재 상황과 불일치 할 수 있다. 유저는 상태 코드를 통해 다른 정보를 알기 어려우며 에러 발생시 원인이 상태 코드와 불일치 할 수 있다는 걸 명심해야 한다.

[제5장, HTTP와 연계하는 웹 서버]

1대로 멀티 도메인을 가능하게 하는 가상 호스트

HTTP/1.1 에서는 하나의 HTTP 서버에 여러 개의 웹 사이트를 실행할 수 있다. 이를 위해 가상 호스트라는 기능을 사용한다.


*가상 호스트 (Virtual Host) :
물리적으로는 서버가 한 대이지만 가상으로 여러대가 있는 것처럼 설정하는 것이 가능하다.
 HTTP 리퀘스트를 보내는 경우에는 호스트명과 도메인 명을 완전하게 포함한 URI를 지정하거나, 반드시 Host 헤더 필드에서 지정해야 한다.
 
 

 
HTTP 는 클라이언트와 서버 이외에 프록시, 게이트웨이, 터널 같은 통신을 중계하는 프로그램과 서버를 연계하는 것도 가능하다. 이러한 프로그램과 서버는 그 다음에 있는 다른 서버에 리퀘스트를 중계하고, 그 서버로부터 받은 리스폰스를 클라이언트에 반환하는 역할을 담당한다.

*프록시:  서버와 클라이언트의 양쪽 역할을 중계하는 프로그램으로, 클라이언트로의 리퀘스트를 서버에 전송하고, 그에 대한 서버의 리스폰스를 클라이언트에 전송한다. 받은 리퀘스트 URI 를 변경하지 않고 그 다음의 리소스를 가지고 있는 서버에 보낸다.

 리소스의 본체를 가진 서버는 오리진 서버라고 한다. 오리진 서버로부터 되돌아온 리스폰스는 프록시 서버를 경유해서 클라이언트에 돌아온다.

*사용 이유
(1) 캐시를 이용해 네트워크 대역을 효율적으로 사용하기 위함
(2) 조직 내 특정 웹 사이트에 대한 액세스를 제한하기 위함
(3) 액세스 로그를 획득하는 정책을 지키기 위함

프록시의 사용방법은 2개의 기준으로 분류한다.

  • 캐시하는지의 여부 (캐싱 프록시)
  • 메시지를 변경하는지의 여부 (투명 프록시)

 캐싱 프록시 : 프록시로 리스폰스를 중계하는 때에는 프록시 서버 상에 리소스 캐시를 보존해두는 타입의 프록시이다. 프록시에 다시 같은 리소스에 리퀘스트가 온 경우, 오리진 서버로부터 리소스를 획득하지 않고 캐시를 리스폰스로 되돌려 준다.

 투명 프록시 : 프록시로 리퀘스트와 리스폰스를 중계할 때 메시지 변경을 하지 않는 타입의 프록시이다. 반대로는 비투과 프록시라고 한다.

*게이트웨이 :  다른 서버를 중계하는 서버로 클라이언트로부터 수신한 리퀘스트를 리소스를 보유한 서버인 것처럼 수신한다. 그래서 클라이언트는 상대가 게이트웨이라는 것을 알지 못하는 경우도 있다. 게이트웨이의 동작은 프록시와 매우 유사하다. 게이트웨이는 그 다음에 있는 서버가 HTTP 서버 이외의 서비스를 제공하는 (ex. DB 에 접속해 쿼리를 사용해 데이터를 얻는 경우, 쇼핑 사이트 등에서 신용 카드 결제 시스템 등과 연계하는 경우) 서버가 된다.

*터널:  서로 떨어진 두 대의 클라이언트와 서버 사이를 중계하며 접속을 주선하는 중계 프로그램이다. SSL 과 같은 암호화 통신을 통해 서버와 안전하게 통신을 하기 위해 사용한다. 통신 중인 양쪽 끝의 접속이 끊어질 때 종료된다.

리소스를 보관하는 캐시

*캐시 (Cache) : 프록시 서버와 클라이언트의 로컬 디스크에 보관된 리소스의 사본을 가리킨다. 통신량과 통신 시간을 절약할 수 있다.

*캐시 서버
: 프록시 서버의 일종 = 캐싱 프록시
: 프록시가 리스폰스 중계할 때 프록시 서버 상의 리소스 사본을 보존한다.
: 캐시를 이용함으로써 서버는 같은 리퀘스트를 매번 처리하지 않아도 된다.

*캐시의 유효기간
: 캐시되어 있는 리소스에는 유효성이 있다.
: 클라이언트의 요구나 캐시 유효 기간 등에 의해 오리진 서버의 유효성 확인, 새로운 리소스 획득이 필요하다.

*클라이언트 측의 캐시
: Ex. 인터넷 익스플로러의 인터넷 임시 파일
: 브라우저가 유효한 캐시를 가진 경우 - 같은 리소스의 액세스는 서버가 아닌 로컬 디스크로부터 불러온다.

[제6장, HTTP 헤더]

HTTP 메세지 헤더

HTTP 프로토콜의 리퀘스트와 리스폰스에는 반드시 메시지 헤더가 포함된다. 메세지 헤더는 클라이언트나 서버가 리퀘스트나 리스폰스를 처리하기 위한 정보를 포함한다.

*리퀘스트의 HTTP 메시지 : 메소드 + URI + HTTP 버전 + HTTP 헤더 필드로 구성

*리스폰스의 HTTP 메시지 : HTTP 메시지 + HTTP 버전 + 상태코드(코드와 설명) + HTTP 헤더 필드로 구성

*HTTP 헤더 필드 : HTTP 메시지에 대한 정보를 갖는다. 리퀘스트와 리스폰스 모두에 존재하며 HTTP 버전과 확장 사양에 따라 지원 내용이 달라진다.

HTTP 헤더 필드

HTTP 헤더 필드는 헤더 필드명과 필드 값으로 구성되어 있고 콜론으로 나뉘어져 있다. 메세지 바디의 크기, 사용하고 있는 언어, 인증 정보 등을 포함한다. 

헤더필드명 : 필드 값
Content-Type:text/html

아래와 같이 하나의 헤더 필드가 여러 개의 필드 값을 가질 수도 있음
Keep-Alive:timeout=15,max=100


HTTP 헤더 필드 종류

  1. 일반적 헤더 필드 (General Header Fields) : 리퀘스트 메시지와 리스폰스 메시지 둘 다 사용되는 헤더
  2. 리퀘스트 헤더 필드 (Request Header Fields) : 리퀘스트 메시지에 사용되는 헤더. 리퀘스트의 부가적 정보 / 클라이언트의 정보 / 리스폰스 콘텐츠에 관한 우선순위를 포함한다.
  3. 리스폰스 헤더 필드 (Response Header Fields) : 리스폰스 메시지에 사용되는 헤더. 리스폰스의 정보 / 서버의 정보 / 클라이언트의 추가 정보를 포함한다.
  4. 엔티티 헤더 필드 (Entity Header Fields) : 리퀘스트 메시지와 리스폰스 메시지에 포함된 엔티티에 사용되는 헤더. 콘텐츠 갱신 시간 등 엔티티에 대한 정보를 포함한다.

End-to-end 헤더 & Hop-by-hop 헤더

*End-to-end 헤더 : 리퀘스트나 리스폰스의 최종 수신자에게 전송된다. 캐시에서 구축된 리스폰스 중 보존되며, 다시 전송해야 하는 구조이다.

*Hop-by-hop 헤더 : 한 번의 전송에 대해서만 유효하며 캐시와 프록시에 의해 전송되지 않는 것도 있다.

HTTP/1.1 일반 헤더 필드

일반 헤더 필드는 리퀘스트 메세지와 리스폰스 메세지 양쪽에서 사용되는 헤더이다. 

*Cache-Control 헤더: 디렉티브로 불리는 명령을 사용하여 캐싱 동작을 지정한다. 디렉티브가 여러개일 경우 콤마(,)로 구분한다. 리퀘스트 및 리스폰스 할 때에 사용 가능하다.

Cache-Control: private, max-age=0, no-cache


캐시가 가능한 지 여부를 나타내는 디렉티브 (Cache-Control 디렉티브)

1) 캐시 리퀘스트 디렉티브

디렉티브 파라미터 설명
no-cache 없음 오리진 서버에 강제적인 재검증
no-store 없음 캐시는 리퀘스트, 리스폰스의 일부분을 보존해서는 안됨
max-age = [초] 필수 리스폰스의 최대 age 값
max-state (= [초]) 생략 가능 기한이 지난 리스폰스를 수신
min-fresh = [초] 필수 지정한 시간 이후에 변경된 리스폰스를 보관
no-transform 없음 프록시는 미디어 타입을 변환해서는 안됨
only-if-cached 없음 캐시에서 리소스를 취득
cache-extension - 새로운 디렉티브를 위해서 토큰


2) 캐시 리스폰스 디렉티브

디렉티브 파라미터 설명
public 없음 어딘가에 리스폰스 캐시 가능
private 생략 가능 특정 유저에 대해서만 리스폰스
no-cache 생략 가능 유효성의 재확인 없이는 캐시는 사용해서는 안됨
no-store 없음 캐시는 리퀘스트, 리스폰스의 일부분을 보존해서는 안됨
no-transform 없음 프록시는 미디어 타입을 변경해서는 안됨
must-revalidate 없음 캐시 가능하지만 오리진 서버에 리소스의 재확인을 요구
proxy-revalidate 없음 중간 캐시 서버에 대해서 캐시했던 리스폰스의 유효성의 재확인을 요구
max-age = [초] 필수 리스폰스의 최대 Age 값
s-maxage = [초] 필수 공유 캐시 서버의 리스폰스 최대 Age 값
cache-extension - 새로운 디렉티브를 위한 토큰


Cache-Control: no-cache 디렉티브가 클라이언트 리퀘스트, 서버 리스폰스에서 사용됐을 때 각각의 동작

 클라이언트의 리퀘스트로 no-cache 디렉티브가 사용된 경우, 캐시된 리스폰스를 클라이언트가 받아들이지 않음을 나타낸다. 즉, 중간 캐시 서버가 오리진 서버까지 리퀘스트를 전송해야 한다.

 서버의 리스폰스에 no-cache 디렉티브가 사용된 경우, 캐시 서버는 리스폰스를 저장할 수 없다. 오리진 서버는 캐시 서버가 그 이후의 리퀘스트에서 리소스의 유효성을 재확인하지 않고는 그 리스폰스를 사용하지 못하도록 한다.

Connection 헤더필드의 두 가지 역할

1) 프록시에 더 이상 전송하지 않는 헤더 필드 (hop-by-hop)를 지정한다.
2) HTTP/1.1 에서는 지속적 접속은 디폴트값이다. 그래서 리퀘스트를 송신한 클라이언트의 접속은 유지되고, 추가 리퀘스트를 송신한다. 만약 서버측에서 명시적으로 접속을 끊고 싶다면 Connection 헤더 필드에 Close 라고 지정하면 된다.

(+HTTP/1.1 이전에는 지속적 접속을 원한다면 Connection 필드에 Keep-Alive 를 지정해야 했다.)

Upgrade 헤더 필드를 사용해야 하는 이유

 Upgrade 헤더 필드는 HTTP 및 다른 프로토콜의 새로운 버전이 통신에 이용되는 경우 사용된다. 지정하는 대상이 전혀 다른 통신 프로토콜이어도 상관 없다.

 Upgrade 헤더 필드에 의해서 업그레이드 되는 대상은 클라이언트와 인접한 서버 사이기 때문에 Upgrade 헤더 필드를 사용하는 경우 Connection:Upgrade 도 지정할 필요가 있다.

Via 헤더 필드를 사용해야 하는 이유

 Via 헤더 필드는 클라이언트와 서버 간의 리퀘스트 혹은 리스폰스 메시지의 경로를 알기 위해 사용한다. 프록시 혹은 게이트웨이는 자신의 서버 정보를 Via 헤더 필드에 추가한 뒤 메시지를 전송한다. Via 헤더 필드는 전송된 메시지의 추적과 리퀘스트 루프의 회피 등에 사용되기 때문에 프록시를 경유하는 경우에는 반드시 추가해줘야 한다.

리퀘스트 헤더 필드

 리퀘스트 헤더 필드는 클라이언트 측에서 서버 측으로 송신된 리퀘스트 메시지에 사용하는 헤더로, 리퀘스트의 부가 정보와 클라이언트의 정보, 리스폰스의 콘텐츠에 관한 우선 순위 등을 추가한다.

*Accept : 유저 에이전트에 처리할 수 있는 미디어 타입과 미디어 타입의 상대적인 우선 순위를 전달하기 위해 사용된다. 미디어 타입에는 텍스트 파일, 이미지 파일, 동영상 파일 등이 있다.

*Accept-Language : 유저 에이전트가 처리할 수 있는 자연어의 세트 (한국어와 영어) 와 자연어 세트의 상대적인 우선순위를 전달하기 위해 사용한다. 자연어 세트는 한 번에 여러개 지정이 가능하다.

Accept-Language: ko-kr, en-us;q=0.8,en;q=0.3


 위 예시는 한국어 리소스가 있는 경우 한국어로, 없으면 영어 리소스로 리스폰스를 받고 싶다는 것을 나타낸다.

HTTP/1.1 에서 유일한 필수 헤더 필드 Host

 Host 필드는 리퀘스트한 리소스의 인터넷 호스트와 포트 번호를 전달하는 헤더 필드이다. 이 필드는 HTTP/1.1 에서 유일한 필수 헤더 필드인데, 1대의 서버에서 복수의 도메인을 할당할 수 있는 가상 호스트 구조와 깊은 관련이 있다.

 한 IP 주소에 복수개의 도메인이 적용되어 있다고 한다면, 리퀘스트가 서버에 왔을 때 어느 도메인에 대한 리퀘스트인지 알 수 없다. 그래서 Host 헤더 필드에 리퀘스트를 받을 호스트명을 명확하게 기재해야 하는 것이다. 만약 호스트명이 설정되어 있지 않은 경우 < Host: > 와 같이 값을 비워서 보낸다.

If-Match, If-None-Match, If-Modified-Since, If-Unmodified-Since, If-Range

If-xxx 서식의 리퀘스트 헤더 필드는 조건부 리퀘스트이다. 지정된 조건에 맞는 경우에만 리퀘스트를 받는다.

1) If-Match : 조건부 리퀘스트의 하나로 서버 상의 리소스를 특정하기 위해 엔티티 태그 값을 전달한다. 서버는 If-Match 의 필드 값과 리소스의 ETag 값이 일치한 경우에만 리퀘스트를 받아들일 수 있고, 불일치할 경우 상태코드 412 Precondition Failed 리스폰스를 반환한다.

2) If-None-Match : If-Match 헤더 필드와 반대로 동작한다. 필드값에 지정된 ETag 값이 지정된 리소스의 ETag 값과 일치하지 않으면 리퀘스트를 받아들인다.

3) If-Modified-Since : 리소스 갱신 날짜가 필드값보다 새롭지 않다면 리퀘스트를 받아들인다. 만약 필드 지정 날짜 이후 지정 리소스가 갱신되어있지 않다면 304 리스폰스를 반환한다.

4) If-Unmodified-Since : If-Modified-Since 와 반대로 동작한다. 지정된 리소스가 필드 값에 지정된 날짜 이후에 갱신되어 있지 않은 경우에만 리퀘스트를 받아들인다.

5) If-Range : 필드값과 지정한 리소스의 ETag 값 혹은 날짜가 일치하면 Range 리퀘스트로 처리한다. 일치하지 않을 경우 전체 리소스를 반환한다.

리스폰스 헤더 필드

리스폰스 헤더 필드는 서버 측에서 클라이언트 측으로 송신된 리스폰스 메시지에 사용하는 헤더이다. 리스폰스의 부가 정보, 서버의 정보, 클라이언트에 부가정보 요구 등을 나타낸다.

 1) ETag 헤더 필드 : 엔티티 태그라고 불리며 일의적(가장 중요한 의미의) 리소스를 특정하기 위한 문자열을 전달한다. 서버는 리소스마다 ETag 값을 할당한다. 리소스가 갱신되면 ETag 값도 갱신해야 한다. 도중에 다운로드가 끊긴 경우, ETag 값을 참조해 리소스를 특정한다.

- 강한 ETag : 엔티티가 조금 다르더라도 반드시 값이 변화한다.
- 약한 ETag : 리소스가 같다는 것만을 나타낸다. 의미가 다른 리소스일 경우만 값이 변화하며, 값의 앞부분에 "W/"가 붙는다.

2) Location 헤더 필드 : 리스폰스의 수신자에 대해 Request-URI 이외의 리소스 액세스를 유도하는 경우에 사용한다.

3) Vary 헤더 필드 : 캐시를 컨트롤하기 위해 사용한다. 오리진 서버가 프록시 서버에 로컬 캐시를 사용하는 방법에 대한 지시를 전달한다. 오리진 서버로부터 Vary에 지정된 리스폰스를 받아들인 프록시 서버는 이후 캐시된 리퀘스트와 같은 vary에 지정된 헤더 필드를 가진 리퀘스트에 대해서만 캐시 반환이 가능하다. 같은 리소스에 대한 리퀘스트라도, Vary에 지정된 헤더 필드가 다른 경우 오리진 서버로부터 리소스를 취득해야한다.

엔티티 헤더 필드

 엔티티 헤더 필드는 리퀘스트 메시지와 리스폰스 메시지에 포함된 엔티티에 사용하는 헤더로 콘텐츠의 갱신 시간 같은 엔티티에 관한 정보를 포함한다.

*Content-MD5 : 메시지 바디가 변경되지 않고 도착했는 지 확인하기 위해 MD5 알고리즘에 의해서 생성된 값을 전달한다. 메시지 바디에 MD5 알고리즘을 적용해서 얻은 128 비트의 바이너리 값에 Base64 인코딩을 한 결과를 필드값에 기록한다. HTTP 헤더에는 바이너리 값을 기록하는 것이 불가능하기 때문에 Base64로 인코딩하고 있다.

 하지만 이 방식에서는 우발적으로 콘텐츠가 변경되어 버린 사실은 알 수 있지만 악의를 가진 변조는 검출할 수 없다는 약점이 있다. 콘텐츠를 변조하면 MD5 도 재계산해서 벼내조하는 것이 가능하기 때문이다.

쿠키 헤더 필드

쿠키는 유저 식별과 상태 관리에 사용한다. 쿠키 호출시 쿠키의 유효 기한과 송신지 도메인, 경로, 프로토콜 등을 체크하기 때문에 데이터 도난의 위험이 적어진다. 

*Set-Cookie 의 Expire 속성 : 브라우저가 쿠키를 송출할 수 있는 유효기간을 지정할 수 있다. 이 속성을 생략한 경우에는 브라우저 세션이 유지되고 있는 동안만 유효하게 된다. 통상 브라우저를 닫을 때까지 이다. 

 또한 한 번 서버에 송출한 클라이언트의 쿠키는 서버에서 명시적으로 삭제하는 방법은 없다. 그래서 유효 기간이 지났다면 쿠키를 덮어 쓰는 방식으로 삭제해야 한다.

*Set-Cookie 의 Domain 속성 : 도메인 속성에 의해서 지정된 도메인명은 후방 일치가 된다. 예를 들어, "example.com" 으로 지정했을 때 "www.example.com:" 과 "www2.example.com" 등에서도 쿠키가 송출된다. 

 그렇기 때문에 명시적으로 여러 도메인에 대해서 쿠키를 송출하는 경우를 제외하고 도메인 속성은 지정하지 않는 것이 안전하다.

*Set-Cookie 의 Secure 속성 : 웹 페이지가 HTTPS 에서 열렸을 때에만 쿠키 송출을 제한하기 위해 지정한다.

*Set-Cookie 의 HttpOnly 속성 : 자바스크립트를 경유해서 쿠키를 취득하지 못하도록 하는 쿠키의 확장 기능이다. 크로스 사이트 스크립팅 (XSS) 으로부터 쿠키의 도청을 막는 것이 목표이다. 이 속성이 부여된 쿠키는 자바스크립트의 [document.cookie] 에서는 읽어들일 수 없다. 그렇기 때문에 XSS 에서 자바스크립트를 이용해 쿠키를 훔칠 수 없다.

*XSS (Cross-site scripting) : 공격자가 상대방의 브라우저에 스크립트가 실행되도록 해 사용자의 세션을 가로채거나, 웹사이트를 변조하거나, 악의적 콘텐츠를 삽입하거나, 피싱 공격을 진행하는 것을 말한다. XSS 공격은 스크립트 언어와 취약한 코드를 공격 대상으로 한다. 웹 사이트의 관리자가 아닌 이가 웹 페이지에 악성 스크립트를 삽입하는 공격유형이다.

댓글