등장 배경, HTTP/1.1과의 차이점, 현재까지 알려진 보안 이슈에 대해 알아보자.
등장 배경
- HTTP/1.1의 메시지 포맷은 구현의 단순성과 접근성에 주안점을 두고 최적화됨.
- 커넥션 하나로 요청 하나를 보내고 그에 대한 응답 하나만을 받는 HTTP의 메시지 교환 방식은 단순하지만, 응답을 받아야한 그 다음 요청을 보낼 수 있기 때문에 심각한 회전 지연(latency)이 있음.
- 이 문제를 회피하기 위해 병렬 커넥션이나 파이프라인 커넥션이 도입되었지만 성능 개선에 대한 근본적인 해결은 되지 못함.
- 구글은 SPDY(스피디)라는 프로토콜을 내놓았고 SPDY는 기존의 HTTP에 속도를 개선하기 위한 여러 기능을 추가한 것임.
- 헤더 압축을 통해 대역폭을 절약.
- 하나의 TCP 커넥션에 여러 요청을 동시에 보냄.
- 클라이언트가 요청을 보내지 않아도 서버가 능동적으로 리소스를 푸시.
- SPDY를 적용했을 때 RTT(round trip delay time)가 20ms인 상황에서 12.34%의 성능 개선이 있었음.
- 2012년 HTTP 작업 그룹은 SPDY를 기반으로 HTTP/2.0 프로토콜을 설계하기로 결정함.
개요
- HTTP/2.0은 서버와 클라이언트 사이의 TCP 커넥션 위에서 동작함.
- TCP 커넥션을 초기화하는 것은 클라이언트임.
- HTTP/2.0 요청과 응답은 길이가 정의된(최대 16363 바이트) 한 개 이상의 프레임에 담김.
- HTTP 헤더는 압축되어 담김.
- 프레임에 담긴 요청과 응답은 스트림을 통해 보내짐. 한 개의 스트림이 한 쌍의 요청과 응답을 처리함.
- 하나의 커넥션 위에 여러 개의 스트림이 동시에 만들어질 수 있음. 즉 여러개의 요청과 응답을 동시에 처리하는것이 가능함.
- 스트림에 대한 흐름 제어와 우선순위 부여 기능도 제공함.
- 서버 푸시를 도입함. 이를 통해 서버는 클라이언트에게 필요하다고 생각하는 리소스라면 그에 대한 요청을 명시적으로 받지 않더라도 능동적으로 클라이언트에게 보내줄 수 있음.
- 호환성 유지를 위해 HTTP/2.0은 요청과 응답 메시지의 의미를 HTTP/1.1과 같도록 유지하고 있음.
- Content-Length 헤더의 이름은
:cotent-length
가 됨. - 상태줄을 표현하던 404 Not Found는 404 값을 갖고 있는
:status
헤더로 표현하게 됨.
HTTP/1.1과의 차이점
프레임

- 모든 메시지는 프레임에 담겨 전송됨. 모든 프레임은 8 바이트 크기의 헤더로 시작하며, 최대 16383 바이트 크기의 페이로드가 옴.
- 프레임 헤더의 각 필드는 아래와 같음.
- R : 예약된 2비트 필드, 반드시 0 이어야함.
- 길이 : 페이로드의 길이를 타나내느 14비트 무부호 정수 (이 길이에 프레임 헤더는 포함되지 않음)
- 종류 : 프레임의 종류.
- 플래그 : 플래그 값의 의미는 프레임의 종류에 따라 다름.
- R : 예약된 1비트 필드, 반드시 0 이어야함.
- 스트림 식별자 : 31비트 스트림 식별자. 특별히 0은 커넥션 전체와 연관된 프레임을 의미.
- HTTP/2.0은 DATA, HEADERS, PRIORITY, RST_STREAM, SETTINGS, PUSH_PROMISE, PING, GOAWAY, WINDOW_UPDATE, CONTINUATION 이라는 총 10가지 프레임을 정의하고 있으며, 페이로드 형식이나 내용은 프레임의 종류에 따라 다름.
스트림과 멀티플렉싱
- 스트림은 HTTP/2.0 커넥션을 통해 클라이언트와 서버 사이에서 교환되는 프레임들의 독립된 양방향 시퀀스임.
- 한 쌍의 HTTP 요청과 응답은 하나의 스트림을 통해 이루어짐.
- HTTP/1.1은 한 커넥션을 통해 요청을 보냈을 때, 그에 따른 응답이 도착하고 나서야 같은 TCP 커넥션으로 다시 요청을 보낼 수 있음. 이로 인해 한 페이지에서 보내야 할 요청이 수십에서 수백에 달하는 오늘날에 회전 지연이 불가피함.
- HTTP/2.0에서는 하나의 커넥션에 여러 개의 스트림이 동시에 열림.
- 스트림은 우선순위를 가질 수 있음. 단 요청이 우선 순위대로 처리된다는 보장은 없음.
- 모든 스트림은 31비트의 무부호 정수로된 고유한 식별자를 가짐.
- 스트림이 클라이언트에 의해 초기화된 경우 이 식별자는 반드시 홀수여야 함.
- 스트림이 서버에 초기화된 경우 이 식별자는 반드시 짝수여야 함.
- 새로 만들어지는 스트림의 식별자는 이전에 만들어졌거나 예약된 스트림들의 식별자보다 커야 함.
- 위 규칙을 어기는 식별자를 받았다면 에러 코드가 PROTOCOL_ERROR인 커넥션 에러로 응답해야 함.
- 서버와 클라이언트는 스트림을 상대방과 협상 없이 일방적으로 만듦.
- 협상을 위해 TCP 패킷을 주고받느라 시간을 낭비하지 않아도 됨.
- HTTP/2.0 커넥션에서 한번 사용한 스트림 식별자는 다시 사용할 수 없음.
- 식별자가 고갈되면 커넥션을 다시 맺으면 됨.
- 동시에 여러개의 스트림을 사용하면 스트림이 블록되는 문제가 있을 수 있으나, WINDOW_UPDATE 프레임을 통해서 흐름 제어를 하여 스트림들이 서로 간섭해서 망가지는 것을 막아줌.
헤더 압축
- HTTP/2.0에서 HTTP 메시지의 헤더를 압축하여 전송함.
- 헤더는 HPACK 명세에 정의된 헤더 압축 방법으로 압축된 뒤 ‘헤더 블록 조각’들로 쪼개져서 전송됨. 받는쪽에서는 이 조각들을 이은 뒤 압축을 풀어 원래의 헤더 집합으로 복원함.
HTTP 2.0의 기반이 된 SPDY 프로토콜에서는 헤더를 deflate 알고리즘으로 압축함. 하지만 이는 CRIME(HTTP 쿠키 관련)에 취약해 HPACK 명세에 따라 헤더를 압축함.
- HPACK은 헤더를 압축하고 해제할 때 “압축 컨텍스트”를 사용함.
- 오동작하지 않으려면 올바른 압축 컨텍스트를 유지해야 함.
- 압축 컨텍스트는 수신한 헤더의 압축을 풀면 이에 영향을 받아 바뀜.
- 송신측은 압축 컨텍스트가 변경되었다고 가정함. 따라서 헤더를 받은 수신측은 반드시 압축 해제를 수행해야 함. 만약 그럴 수 없다면 반드시 COMPRESSION_ERROR와 함께 커넥션을 끊어야 함.
서버 푸시
- HTTP/2.0은 서버가 하나의 요청에 대해 응답으로 여러 개의 리소스를 보낼 수 있도록 해줌.
- 서버가 클라이언트에서 어떤 리소스를 요구할 것인지 미리 알 수 있는 상황에서 유용함.
- 예를 들어 HTML 문서를 요청 받은 서버는 그 HTML 문서가 링크하고 있는 이미지, CSS 파일, 자바스크립트 파일 등의 리소스를 클라이언트에게 푸시할 수 있음.
- 서버는 클라이언트에게 자원을 푸시할 것임을 PUSH_PROMISE 프레임을 보내어 미리 알려줘야 함.
- 클라이언트가 PUSH_PROMISE 프레임을 받게 되면 해당 프레임의 스트림은 클라이언트 입장에서는 “예약됨(원격)” 상태가 됨.
- 이 상태에서 클라이언트는 RST_STREAM 프레임을 보내어 푸시를 거절할 수 있음. RST_STREAM을 보내게 되면 그 스트림은 즉각 닫히게 됨.
- 사전에 PUSH_PROMISE 프레임을 보내는 이유는 서버가 푸시하려고 하는 자원을 클라이언트가 별도로 또 요청하게 되는 상황을 피하기 위함임.
- 서버 푸시를 사용할 때 주의사항
- 서버 푸시를 사용하기로 했더라도, 중간에 프락시가 서버로부터 받은 추가 리소스를 클라이언트에게 전달하지 않을 수 있으며, 반대로 아무런 추가 리소스를 서버로부터 받지 않았음에도 클라이언트에게 추가 리소스를 전달할 수 있음.
- 서버는 안전하고 캐시 가능하고 본문을 포함하지 않은 요청에 대해서만 푸시를 할 수 있음.
- 푸시할 리소스는 클라이언트가 명시적으로 보낸 요청과 연관된 것이어야 함. 서버가 보내는 PUSH_PROMISE 프레임은 원 요청을 위해 만들어진 스트림을 통해 보내짐.
- 클라이언트는 반드시 서버가 푸시한 리소스를 동일 출처 정책(Same Origin Policy)에 따라 검사해야 함.
- 서버 푸시를 끄고 싶다면 SETTINGS_ENABLE_PUSH을 0으로 설정하면 됨.
알려진 보안 이슈
- 중개자 캡슐화 공격(intermediary Encapsulation Attacks)
- HTTP/2.0 메시지를 중간의 프락시가 HTTP/1.1 메시지로 변환할 때 메시지의 의미가 변질될 가능성이 있음.
- HTTP/2.0은 헤더 필드의 이름과 값을 바이너리로 인코딩함. 이는 HTTP/2.0이 헤더 필드로 어떤 문자열이든 사용할 수 있게 해줌. 이는 정상적인 HTTP/2.0 요청이나 응답이 불법적이거나 위조된 HTTP/1.1 메시지로 변역되는 것을 유발할 수 있음.
- 긴 커넥션 유지로 인한 개인정보 누출 우려
- HTTP/2.0은 사용자가 요청을 보낼 때의 회전 지연을 줄이기 위해 클라이언트와 서버 사이의 커넥션을 오래 유지하는 것을 염두에 두고 있음. 이것은 개인정보의 유출에 악용될 가능성이 있음.
- ex) 어떤 사용자가 브라우저를 사용할 때, 그 사용자는 이전에 그 브라우저를 사용했던 사용자가 무엇을 했는지 알아내는 경우.
HTTP/2.0를 도입하면서 많은 문제를 해결했지만 TCP 프로토콜 자체의 한계 때문에 더 이상 성능 개선에도 한계가 오게 됨 → UDP 사용