웹소켓
"오늘의AI위키"의 AI를 통해 더욱 풍부하고 폭넓은 지식 경험을 누리세요.
1. 개요
웹소켓은 HTML5 사양에서 TCP 기반 소켓 API를 대체하기 위해 개발된 프로토콜이다. 2008년 마이클 카터가 주도한 토론을 통해 최초 버전이 탄생했으며, 이안 힉슨에 의해 HTML5 명세에 포함되었다. 웹소켓은 코멧 채널의 복잡성과 비효율성을 해결하며 웹 보안을 유지하는 것을 목표로 한다. 2011년 RFC 6455로 표준화되었으며, JavaScript API를 제공한다. 웹소켓은 개시 핸드셰이크, 데이터 메시지 전송, 종료 핸드셰이크를 통해 연결을 설정하며, 프레임 기반 메시지를 사용해 텍스트 또는 바이너리 데이터를 전송한다. 웹 브라우저의 웹 API를 통해 웹소켓 서버에 연결할 수 있으며, 다양한 프로그래밍 언어와 서버 환경에서 구현된다. 보안을 위해 동일 출처 정책의 제한을 받지 않으므로 Origin 헤더 검증 및 인증 메커니즘을 통해 크로스 사이트 웹소켓 하이재킹 공격을 방지해야 한다.
더 읽어볼만한 페이지
- 2011년 컴퓨팅 - 3·3 DDoS 공격
3·3 DDoS 공격은 2011년 3월 4일에 시작되어 3월 7일로 공격 시점을 앞당겨 실행된 분산 서비스 거부 공격으로, 악성 코드는 P2P 파일 공유 사이트를 통해 유포되었으며 공격 명령에는 보호나라 사이트 접속 차단 명령이 추가되었다. - HTML5 - 구글 스위피
구글 스위피는 구글에서 개발한 웹 서비스로, SWF 파일을 JSON으로 직렬화한 후 자바스크립트를 통해 SVG로 변환하여 애니메이션을 구현하는 기술이었으나 2016년 7월 1일 서비스가 종료되었다. - HTML5 - 웹 스토리지
웹 스토리지는 웹 브라우저에서 클라이언트 측에 데이터를 저장하는 API로, 쿠키와 유사하지만 더 큰 저장 용량을 제공하며 로컬 스토리지와 세션 스토리지로 구분된다. - 웹 개발 - Ajax
Ajax는 웹 페이지 전체를 새로고침하지 않고 비동기적으로 서버와 통신하여 웹 애플리케이션의 일부를 업데이트하는 웹 개발 기술로, XMLHttpRequest 객체의 등장으로 가능해졌으며 HTML, CSS, DOM, JavaScript, JSON 등의 기술을 통합하여 동적인 사용자 인터페이스를 구현한다. - 웹 개발 - WebXR
WebXR은 웹 브라우저에서 가상 현실 및 증강 현실 콘텐츠를 구현하기 위한 API로, 다양한 장치 및 플랫폼에서 몰입형 웹 경험을 제공하며, 구글, 메타, 모질라 등 여러 기업과 단체가 개발에 참여하여 지속적인 업데이트를 통해 기능 향상을 목표로 한다.
| 웹소켓 | |
|---|---|
| 일반 정보 | |
| 이름 | 웹소켓 |
| 종류 | 컴퓨터 네트워크 프로토콜 |
| 개발 | IETF |
| 연결 | TCP |
![]() | |
| 상세 정보 | |
| 관련 문서 | |
| RFC 6455 | |
2. 역사
웹소켓은 HTML5의 일부로 제안되었으며, XMLHttpRequest의 단점을 해결하고 기존의 코멧 등을 대체하기 위해 개발되었다.
Ajax 애플리케이션에서는 서버와 클라이언트 간 데이터 교환이 빈번하지만, XMLHttpRequest는 브라우저에서 서버로 요청을 보내는 데 그쳐 서버에서 클라이언트로 데이터를 푸시하는 것이 어려웠다. 코멧은 서버 푸시가 가능했지만, 통신 시마다 TCP 핸드셰이크를 다시 수행하고 HTTP 커넥션을 장시간 점유하는 문제가 있었다.
웹소켓은 서버와 클라이언트가 한 번 연결되면, 그 연결 위에서 전용 프로토콜을 사용하여 통신한다. 새로운 연결을 맺을 필요가 없고, 경량 프로토콜을 사용하므로 통신 손실이 줄어들며, 하나의 연결로 모든 데이터 송수신이 가능하여 다른 애플리케이션에 대한 영향이 적다는 장점이 있다.
2. 1. 주요 연표
웹소켓은 HTML5 사양에서 TCPConnection으로 처음 참조되었다.[96] 2008년 6월, 마이클 카터가 주도한 일련의 논의를 통해 최초 버전의 웹소켓 프로토콜이 탄생했다.[9] "웹소켓"이라는 이름은 이안 힉슨과 마이클 카터가 #whatwg IRC 채팅방에서 협력하여 만들어졌다.[10]웹소켓 이전에는 코멧 채널을 사용하여 80번 포트의 전이중 통신이 가능했지만, 구현이 복잡하고 비효율적이었다. 웹소켓 프로토콜은 이러한 문제를 해결하고 웹의 보안을 유지하는 것을 목표로 한다.
2009년 12월, 구글 크롬 4가 웹소켓을 기본적으로 활성화하여 이 표준을 완전히 지원하는 최초의 브라우저가 되었다.[11] 웹소켓 프로토콜 개발은 2010년 2월 IETF로 이전되었으며, 이안 힉슨이 두 차례 개정했다.[12] 2011년 12월, 가 이안 페트의 지휘 하에 최종 확정되었다.
2010년 11월, 보안 취약점이 발견되어[88] 2010년 12월에 파이어폭스 4와 Opera 11에서 웹소켓이 일시적으로 비활성화되었다. 이후 2011년 1월, 통신 내용을 XOR로 마스킹하는 방법이 도입된 새 버전이 발표되었다. 2011년 8월, 파이어폭스 6가 웹소켓을 다시 지원했지만, Firefox 10까지는 MozWebSocket이라는 이름으로 사용되었다.[89]
W3C에서 시작된 JavaScript용 API 제정은 HTML5와 함께 WHATWG로 이관되었다. 2021년에는 WebSockets Standard가 별도로 분리되었다.[91]
는 메시지별로 DEFLATE 알고리즘을 사용하여 웹소켓에 압축 확장을 도입했다.
3. 프로토콜
웹소켓 프로토콜은 HTTP와 달리 전이중 통신을 사용하며,[93][94] TCP 위에서 메시지 스트리밍을 가능하게 한다.[94] TCP는 바이트 스트림을 다루지만, 웹소켓은 메시지 개념을 도입하여 더 효율적인 통신을 지원한다. 웹소켓 이전에는 코멧 채널을 사용하여 포트 80에서 전이중 통신을 구현했지만, 코멧은 구현이 복잡하고 작은 메시지에 비효율적이었다. 웹소켓 프로토콜은 웹 보안을 유지하면서 이러한 문제를 해결한다.
웹소켓 프로토콜은 암호화되지 않은 연결을 위한 `ws`와 암호화된 연결을 위한 `wss`라는 두 가지 새로운 통합 자원 식별자(URI) 스킴을 정의한다.[95]
웹소켓은 XMLHttpRequest의 단점을 보완하고 Comet을 대체하기 위해 개발되었다. Ajax 애플리케이션에서 서버와 클라이언트 간 데이터 교환이 빈번하지만, XMLHttpRequest는 브라우저에서 서버로 요청을 보내는 역할만 수행하고 서버에서 클라이언트로 데이터를 푸시하는 것은 어려웠다. Comet은 서버 푸시가 가능하지만, TCP 핸드셰이크를 반복하고 HTTP 연결을 오래 점유하여 다른 애플리케이션에 영향을 줄 수 있었다.
반면 웹소켓은 서버와 클라이언트가 한 번 연결을 맺은 후, 전용 프로토콜을 사용하여 통신한다. 이로 인해 새로운 연결을 맺을 필요가 없고, 경량 프로토콜을 사용하므로 통신 손실이 줄어들며, 하나의 연결로 모든 데이터 송수신이 가능하여 다른 애플리케이션에 대한 영향이 적다.
웹소켓 연결 과정은 다음과 같다.
# '''개시 핸드셰이크''' (HTTP 요청 + HTTP 응답)를 통해 연결을 설정한다.
# '''데이터 메시지'''를 통해 애플리케이션 데이터를 전송한다.
# '''종료 핸드셰이크''' (두 개의 닫기 프레임)를 통해 연결을 종료한다.
WebSocket 프로토콜 사양은 '''ws:''' 와 '''wss:'''라는 두 개의 새로운 URI 스키마를 정의하고 있다.[83]
프로토콜은 2011년 5월 완성을 목표로 진행되었지만, 기한을 넘어서도 사양 개정이 계속되어 2011년 7월 11일에 최종 초안 draft-ietf-hybi-thewebsocketprotocol-10이 권고되었지만, 그 후에도 개정이 계속되어 2011년 9월 30일에 draft-ietf-hybi-thewebsocketprotocol-17이 릴리스되었고, 2011년 12월 11일에 proposed standard(표준화 제창)이 되었다.
2010년 11월 26일, draft-ietf-hybi-thewebsocketprotocol-03이나 그 이전의 WebSocket 프로토콜에 보안 허점이 발견되어[88] 2010년 12월에 일시적으로 Firefox 4와 Opera 11의 WebSocket이 비활성화되었고, Chrome은 프로토콜 개정보다 먼저 공격 코드가 나온 경우에는 비활성화한다고 했다. Opera 11은 설정을 활성화하면 사용할 수 있었다. 2011년 1월 11일에 draft-ietf-hybi-thewebsocketprotocol-04가 발표되었고, 서버에 업로드 통신할 때 프록시를 혼란시키지 않기 위해 통신 내용을 XOR로 마스킹하는 방법이 되었다. 2011년 8월 16일에 다시 WebSocket을 지원하는 Firefox 6가 릴리스되었지만, 사양 개정이 계속된다는 이유로 Firefox 10까지 MozWebSocket으로 머리에 Moz가 붙는 형태가 되었다[89] . Firefox for Mobile은 7부터 지원[90] .
웹 브라우저에서 동작하는 JavaScript용 API 제정은, 당초 W3C에서 행해지고 있었다. 그 후, HTML5와 함께 WHATWG로 옮겨가게 되었다.
WHATWG에서는, 당초 주로 HTML Standard 내에서 WebSocket의 API가 규정하고 있었다. 그 후, 2021년에 별개의 사양으로 분리하는 것이 제기되어[91], WebSockets Standard가 창설되게 되었다.
3. 1. 핸드셰이크
웹소켓 연결을 확립하기 위해 클라이언트는 웹소켓 핸드셰이크 요청을 보내고, 서버는 이에 대한 웹소켓 핸드셰이크 응답을 반환한다.[106]클라이언트 요청 예시:[38]
```http
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13
Origin: http://example.com
```
서버 응답 예시:
```http
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=
Sec-WebSocket-Protocol: chat
```
클라이언트는 '''HTTP 요청'''('''메서드''' '''GET''', '''버전 ≥ 1.1''')을 보내고, 서버는 성공적으로 '''상태 코드 101''' (''프로토콜 전환'')을 포함하는 '''HTTP 응답'''을 반환한다. 이는 웹소켓 서버가 HTTP (80) 및 HTTPS (443)와 동일한 포트를 사용할 수 있음을 의미하며, 핸드셰이크가 HTTP와 호환되기 때문이다.[28]
HTTP에서 각 줄은 `\r\n`으로 끝나고 마지막 줄은 비어 있다.
| 측면 | 헤더 | 값 | 필수 여부 |
|---|---|---|
| Origin | [29] | |
| Host | 예 | |
| Sec-WebSocket-Version | 13[30] | |
| Sec-WebSocket-Key | base64-인코딩된 16바이트 임의의 논스[31] | |
| Sec-WebSocket-Accept | base64-인코딩된 (sha1(Sec-WebSocket-Key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"))[32] | |
| Connection | Upgrade[33][34] | |
| Upgrade | websocket[35][36] | |
| Sec-WebSocket-Protocol | 요청에는 클라이언트가 사용하려는 쉼표로 구분된 목록 문자열(선호도 순)이 포함될 수 있으며, 이는 응용 계층 프로토콜(웹소켓 데이터 메시지 상위에 구축됨)을 나타낸다.[37] 클라이언트가 이 헤더를 보내면 서버 응답은 목록의 값 중 하나여야 한다. | 선택 사항 |
| Sec-WebSocket-Extensions | ||
| 기타 헤더 |
`Sec-WebSocket-Key` 및 `Sec-WebSocket-Accept`는 캐싱 프록시가 이전 웹소켓 대화를 다시 전송하는 것을 방지하기 위한 것이며, 인증, 개인 정보 보호 또는 무결성을 제공하지 않는다.
```python3
# Sec-WebSocket-Key를 사용하여 Sec-WebSocket-Accept 계산
from base64 import b64encode
from hashlib import sha1
from os import urandom
# key = b64encode(urandom(16)) # 클라이언트가 이 작업을 수행해야 함
key = b"x3JJHMbDL1EzLkh9GBhXDw==" # 위의 예시 요청의 값
magic = b"258EAFA5-E914-47DA-95CA-C5AB0DC85B11" # 프로토콜 상수
print(b64encode(sha1(key + magic).digest()))
# 출력: HSmrc0sMlYUkAGmm5OPpG2HaGWk=
```
연결이 설정되면 통신은 HTTP 프로토콜을 따르지 않는 이진 프레임 기반 프로토콜로 전환된다.
핸드셰이크는 하이퍼텍스트 전송 프로토콜과 유사하지만, 엄밀히 말하면 다르다. 서버는 처음 HTTP 요청으로 해석한 후, 웹소켓으로 전환한다.
3. 2. 프레임 기반 메시지
웹소켓에서 데이터 전송은 '''데이터 메시지'''와 '''제어 메시지'''를 통해 이루어지며, 이 메시지들은 하나 이상의 프레임으로 구성된다. 효율적인 데이터 전송 및 처리를 위해 '''단편화'''(Fragmentation) 기능이 제공된다.- 비단편화 메시지: `FIN = 1` 및 `opcode ≠ 0`인 단일 프레임으로 구성된다.
- 단편화 메시지: `FIN = 0` 및 `opcode ≠ 0`인 첫 번째 프레임, `FIN = 0` 및 `opcode = 0`인 0개 이상의 중간 프레임, `FIN = 1` 및 `opcode = 0`인 마지막 프레임으로 구성된다.
단편화를 통해 전체 길이를 미리 알 수 없는 메시지를 여러 프레임으로 나누어 전송할 수 있다. 이는 첫 번째 바이트를 전송하기 전에 전체 길이를 알아야 하는 비단편화 메시지와 달리 버퍼링 없이 데이터를 전송할 수 있게 해준다.[40] 또한, 단편화는 여러 스트림을 동시에 멀티플렉싱하여 단일 페이로드가 소켓을 독점하는 것을 방지한다.[41]
제어 프레임 중 닫기(Close) 프레임은 종료 핸드셰이크를 시작하며, 선택적으로 2바이트 사유 코드와 123바이트를 넘지 않는 UTF-8 인코딩 사유 메시지를 포함할 수 있다.[49] 핑(Ping)과 퐁(Pong) 프레임은 지연 시간 측정, keepalive, 하트비트 등에 사용된다.[50][51]
3. 2. 1. 프레임 구조
(비트)(비트)
