Skip to content

Reject invalid Sec-WebSocket-Key values#118

Closed
94xhn wants to merge 1 commit into
Theldus:masterfrom
94xhn:fix-handshake-key-length
Closed

Reject invalid Sec-WebSocket-Key values#118
94xhn wants to merge 1 commit into
Theldus:masterfrom
94xhn:fix-handshake-key-length

Conversation

@94xhn

@94xhn 94xhn commented Jul 14, 2026

Copy link
Copy Markdown

Description

RFC 6455 requires Sec-WebSocket-Key to be a Base64 value that decodes to
exactly 16 bytes. Before this fix, malformed keys could still produce a
101 Switching Protocols response.

This revision rejects a key unless it has the required 24-character padded
form and the existing Base64 decoder produces exactly 16 bytes. The previous
variable-length SHA-1 change is removed, so the accepted key and GUID are
hashed using the existing WS_KEYMS_LEN length.

Verification

  • Reproduced the old behavior with short, invalid-alphabet, and 18-byte keys.
  • Verified the RFC example still produces
    s3pPLMBiTxaQ9kYGzzhZRbK+xOo=.
  • Verified short, overlong, invalid-alphabet, bad-padding, embedded-padding,
    and 18-byte keys are rejected by the complete handshake-response path.
  • Built the library and all examples with CMake/Ninja on MinGW GCC 8.1.0.
  • Compiled the focused regression harness with -Werror -pedantic.

Checklist

  • I've read the notice in the PR template before submitting it
  • My PR is:
    • Trivial and:
      • I've created an issue (please mention the issue number)
      • I haven't created an issue (thats ok...)
    • Non-trivial:
      • Issue number: N/A

get_handshake_accept() always hashed a fixed WS_KEYMS_LEN (60) bytes
of the key+magic-string buffer, regardless of the actual length of
the client-supplied Sec-WebSocket-Key. This only produced the correct
SHA-1 input for keys that happen to be exactly 24 characters (the
common/base64-of-16-bytes case); for any other key length the hash
covered stale/zero bytes beyond the real key+magic-string content,
so the server computed and returned a Sec-WebSocket-Accept value
that does not match RFC 6455 4.1, even though the connection was
otherwise accepted.

Fix by hashing strlen(str) instead of the fixed WS_KEYMS_LEN. Since
str is calloc-zeroed and built via strncpy(str, wsKey, WS_KEY_LEN)
followed by strcat(str, MAGIC_STRING), strlen(str) always equals the
true (possibly truncated) key length plus the magic-string length,
and is bounded by the buffer's allocated size (WS_KEY_LEN + WS_MS_LEN).

Verified with a standalone harness against src/handshake.c: the
standard 24-char RFC6455 example key still yields the well-known
correct Accept value, and a 7-char test key now yields the
independently-computed correct value (previously wrong).
Repository owner deleted a comment from rlaneth Jul 14, 2026
@Theldus

Theldus commented Jul 14, 2026

Copy link
Copy Markdown
Owner

Hi @94xhn,
Thanks for taking a look at wsServer and for submitting this PR.

Unfortunately, per RFC 6455 (section 4.2.1), the Sec-WebSocket-Key must be a base64-encoded value that decodes to exactly 16 bytes:

  1. A |Sec-WebSocket-Key| header field with a base64-encoded (see Section 4 of [RFC4648]) value that, when decoded, is 16 bytes in length.

A canonical base64 encoding of 16 bytes is always 24 characters long. Therefore, accepting a key with a different encoded length would not be compliant with the specs.

What wsServer could do instead, is to reject a connection attempt if provided an invalid key.

@Theldus Theldus closed this Jul 14, 2026
@94xhn 94xhn changed the title Fix wrong Sec-WebSocket-Accept for non-24-char Sec-WebSocket-Key Reject invalid Sec-WebSocket-Key values Jul 15, 2026
@94xhn

94xhn commented Jul 15, 2026

Copy link
Copy Markdown
Author

Thanks for the correction. I reworked the change to reject keys that are not the required 24-character padded Base64 form or do not decode to exactly 16 bytes. The previous variable-length SHA-1 change has been removed, so valid keys use WS_KEYMS_LEN again. I verified the RFC example still produces the expected accept value, while short, overlong, invalid-alphabet, bad-padding, embedded-padding, and 18-byte keys are rejected. The full CMake/Ninja build also passes. GitHub would not let me reopen the PR after updating the branch, so please reopen it if this direction matches what you had in mind.

@94xhn

94xhn commented Jul 15, 2026

Copy link
Copy Markdown
Author

GitHub returned state cannot be changed because the branch was force-pushed or recreated, so the corrected change is now available in #119. Please review the updated implementation there.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants