Skip to content

Commit 1a344a4

Browse files
Emyrknhooyr
authored andcommitted
Reject invalid "Sec-WebSocket-Key" headers from clients
Client "Sec-WebSocket-Key" should be a valid 16 byte base64 encoded nonce. If the header is not valid, the server should reject the client.
1 parent 64ce009 commit 1a344a4

File tree

3 files changed

+37
-8
lines changed

3 files changed

+37
-8
lines changed

Diff for: accept.go

+6-1
Original file line numberDiff line numberDiff line change
@@ -185,10 +185,15 @@ func verifyClientRequest(w http.ResponseWriter, r *http.Request) (errCode int, _
185185
return http.StatusBadRequest, fmt.Errorf("unsupported WebSocket protocol version (only 13 is supported): %q", r.Header.Get("Sec-WebSocket-Version"))
186186
}
187187

188-
if r.Header.Get("Sec-WebSocket-Key") == "" {
188+
websocketSecKey := r.Header.Get("Sec-WebSocket-Key")
189+
if websocketSecKey == "" {
189190
return http.StatusBadRequest, errors.New("WebSocket protocol violation: missing Sec-WebSocket-Key")
190191
}
191192

193+
if v, err := base64.StdEncoding.DecodeString(websocketSecKey); err != nil || len(v) != 16 {
194+
return http.StatusBadRequest, fmt.Errorf("WebSocket protocol violation: invalid Sec-WebSocket-Key %q, must be a 16 byte base64 encoded string", websocketSecKey)
195+
}
196+
192197
return 0, nil
193198
}
194199

Diff for: accept_test.go

+26-7
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"net"
1010
"net/http"
1111
"net/http/httptest"
12+
"nhooyr.io/websocket/internal/test/xrand"
1213
"strings"
1314
"testing"
1415

@@ -36,7 +37,7 @@ func TestAccept(t *testing.T) {
3637
r.Header.Set("Connection", "Upgrade")
3738
r.Header.Set("Upgrade", "websocket")
3839
r.Header.Set("Sec-WebSocket-Version", "13")
39-
r.Header.Set("Sec-WebSocket-Key", "meow123")
40+
r.Header.Set("Sec-WebSocket-Key", xrand.Base64(16))
4041
r.Header.Set("Origin", "harhar.com")
4142

4243
_, err := Accept(w, r, nil)
@@ -52,7 +53,7 @@ func TestAccept(t *testing.T) {
5253
r.Header.Set("Connection", "Upgrade")
5354
r.Header.Set("Upgrade", "websocket")
5455
r.Header.Set("Sec-WebSocket-Version", "13")
55-
r.Header.Set("Sec-WebSocket-Key", "meow123")
56+
r.Header.Set("Sec-WebSocket-Key", xrand.Base64(16))
5657
r.Header.Set("Origin", "https://harhar.com")
5758

5859
_, err := Accept(w, r, nil)
@@ -116,7 +117,7 @@ func TestAccept(t *testing.T) {
116117
r.Header.Set("Connection", "Upgrade")
117118
r.Header.Set("Upgrade", "websocket")
118119
r.Header.Set("Sec-WebSocket-Version", "13")
119-
r.Header.Set("Sec-WebSocket-Key", "meow123")
120+
r.Header.Set("Sec-WebSocket-Key", xrand.Base64(16))
120121

121122
_, err := Accept(w, r, nil)
122123
assert.Contains(t, err, `http.ResponseWriter does not implement http.Hijacker`)
@@ -136,7 +137,7 @@ func TestAccept(t *testing.T) {
136137
r.Header.Set("Connection", "Upgrade")
137138
r.Header.Set("Upgrade", "websocket")
138139
r.Header.Set("Sec-WebSocket-Version", "13")
139-
r.Header.Set("Sec-WebSocket-Key", "meow123")
140+
r.Header.Set("Sec-WebSocket-Key", xrand.Base64(16))
140141

141142
_, err := Accept(w, r, nil)
142143
assert.Contains(t, err, `failed to hijack connection`)
@@ -183,21 +184,39 @@ func Test_verifyClientHandshake(t *testing.T) {
183184
},
184185
},
185186
{
186-
name: "badWebSocketKey",
187+
name: "missingWebSocketKey",
187188
h: map[string]string{
188189
"Connection": "Upgrade",
189190
"Upgrade": "websocket",
190191
"Sec-WebSocket-Version": "13",
191192
"Sec-WebSocket-Key": "",
192193
},
193194
},
195+
{
196+
name: "shortWebSocketKey",
197+
h: map[string]string{
198+
"Connection": "Upgrade",
199+
"Upgrade": "websocket",
200+
"Sec-WebSocket-Version": "13",
201+
"Sec-WebSocket-Key": xrand.Base64(15),
202+
},
203+
},
204+
{
205+
name: "invalidWebSocketKey",
206+
h: map[string]string{
207+
"Connection": "Upgrade",
208+
"Upgrade": "websocket",
209+
"Sec-WebSocket-Version": "13",
210+
"Sec-WebSocket-Key": "notbase64",
211+
},
212+
},
194213
{
195214
name: "badHTTPVersion",
196215
h: map[string]string{
197216
"Connection": "Upgrade",
198217
"Upgrade": "websocket",
199218
"Sec-WebSocket-Version": "13",
200-
"Sec-WebSocket-Key": "meow123",
219+
"Sec-WebSocket-Key": xrand.Base64(16),
201220
},
202221
http1: true,
203222
},
@@ -207,7 +226,7 @@ func Test_verifyClientHandshake(t *testing.T) {
207226
"Connection": "keep-alive, Upgrade",
208227
"Upgrade": "websocket",
209228
"Sec-WebSocket-Version": "13",
210-
"Sec-WebSocket-Key": "meow123",
229+
"Sec-WebSocket-Key": xrand.Base64(16),
211230
},
212231
success: true,
213232
},

Diff for: internal/test/xrand/xrand.go

+5
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package xrand
22

33
import (
44
"crypto/rand"
5+
"encoding/base64"
56
"fmt"
67
"math/big"
78
"strings"
@@ -45,3 +46,7 @@ func Int(max int) int {
4546
}
4647
return int(x.Int64())
4748
}
49+
50+
func Base64(n int) string {
51+
return base64.StdEncoding.EncodeToString(Bytes(n))
52+
}

0 commit comments

Comments
 (0)