Skip to content

Harden JWT, TLS, and dependency security - #642

Open
ahmet wants to merge 1 commit into
novnc:masterfrom
ahmet:harden-jwt-tls-and-dependencies
Open

ahmet wants to merge 1 commit into
novnc:masterfrom
ahmet:harden-jwt-tls-and-dependencies

Conversation

@ahmet

@ahmet ahmet commented Aug 12, 2026 •

Copy link
Copy Markdown

Summary

This is a security hardening pass against v0.13.0. It closes several high-severity issues found in a source review (JWT JWS/JWE confusion, TLS context option overwrite, unpinned jwcrypto with known DoS CVEs, --libserver silently dropping auth) while keeping unauthenticated / non-TLS operation as the default so existing noVNC setups do not break.

Version is bumped to 0.14.0 because of the JWT, TLS, and Python support changes.

High-severity fixes

  • JWTTokenApi is compact JWS only. Encrypted JWE tokens and alg=none are rejected. The verification key is marked use=sig. This is the application-level equivalent of CVE-2022-3102: previously the plugin auto-detected JWE and decrypted with the same key used for signatures, so an RSA/EC private PEM could be abused to inject host/port claims.
  • TLS handshake no longer replaces SSLContext.options. create_default_context() hardening is preserved (options |= extra flags only). --ssl-version default now means TLS 1.2 via ssl.TLSVersion, instead of assigning ssl.OP_ALL (and mixing PROTOCOL_* constants into options).
  • jwcrypto>=1.5.7 (closes CVE-2023-6681, CVE-2024-28102, CVE-2026-39373). numpy, requests, and redis now have minimum versions as well.
  • --libserver no longer nulls token_plugin / auth_plugin, which previously disabled access control with only a generic “option ignored” print.

Other hardening

  • WebSocket data frames capped at 16 MiB; control frames limited to 125 bytes (RFC 6455); fragmented messages and proxy queues (64 MiB) bounded.
  • UnixDomainSocketDirectory uses Path.resolve() + relative_to() and rejects multi-segment tokens.
  • Tokens are no longer written into logs or close reasons.
  • BasicHTTPAuth uses hmac.compare_digest; credentials and Redis passwords can be read from @/path/to/file.
  • Token HTTP lookups use timeout=5 and URL-quote the token.
  • TokenRedis supports TLS (optional 6th source field).
  • Optional --allowed-targets (SSRF allowlist for token-resolved backends) and --max-connections (process cap; default 0 = unlimited).
  • Docker image is python:3.13-slim-bookworm and runs as USER websockify.
  • Python 3.6–3.9 dropped (python_requires >= 3.10); CI covers 3.10–3.14.
  • Added SECURITY.md, Dependabot, and a pip-audit workflow.
  • TCP keepalive options are skipped when the platform lacks the corresponding socket constants (fixes the Darwin TCP_KEEPIDLE test failure).

Breaking / behavioral changes

Change Who is affected
JWTTokenApi rejects JWE Deployments that wrap a signed JWT in JWE
Default min TLS is 1.2 Clients that only speak TLS 1.0/1.1 (opt back in with --ssl-version tlsv1_1)
Python >= 3.10 Packagers still on 3.6–3.9
jwcrypto>=1.5.7 Environments pinning older jwcrypto

Unauthenticated, non---ssl-only operation is unchanged on purpose. Production still needs --ssl-only, --auth-plugin, --allowed-targets, and --max-connections (or a reverse proxy). See SECURITY.md.

Test plan

  • nose2 — 107 tests passed (including new JWT/JWE, alg=none, frame-size, UDS traversal, allowlist, @file auth, Redis TLS-flag tests)
  • flake8
  • pip-audit — no known vulnerabilities on installed jwcrypto 1.5.8 / requests 2.34.2 / numpy 2.5.2 / redis 8.1.0
  • CI on this PR (3.10–3.14 + pip-audit job)
  • If you use JWTTokenApi with JWE today, confirm you can switch to compact JWS
  • If you terminate TLS on websockify itself, confirm clients accept TLS 1.2+

Reject JWE/alg=none in JWTTokenApi, keep TLS 1.2+ defaults from
create_default_context, pin patched jwcrypto, and add optional
target/connection limits without changing unauthenticated defaults.

Co-authored-by: Cursor <cursoragent@cursor.com>
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.

1 participant