Skip to content

Fix malformed Digest Authorization header for multi-value qop (fixes #441)#479

Open
rgaufman wants to merge 1 commit into
nahi:masterfrom
rgaufman:fix-digest-qop-multivalue-441
Open

Fix malformed Digest Authorization header for multi-value qop (fixes #441)#479
rgaufman wants to merge 1 commit into
nahi:masterfrom
rgaufman:fix-digest-qop-multivalue-441

Conversation

@rgaufman

Copy link
Copy Markdown

Fixes #441.

The bug

When a server offers more than one qop-option as a quoted, comma-separated list in the challenge (RFC 2617 / 7616 allow this), e.g.

WWW-Authenticate: Digest realm="...", qop="auth,auth-int", nonce="...", opaque="..."

parse_challenge_param splits the challenge on every comma, including the one inside the quotes. So qop="auth,auth-int" is parsed as qop => %{"auth} (a leading double-quote, truncated) plus a junk auth-int" key. calc_cred then emits a malformed request header:

Authorization: Digest ..., qop="auth, response="<resp>", algorithm=MD5, ...

The qop quote is never closed and it swallows the response= field. Lenient servers ignore the surplus, but stricter ones reject the request. In the wild this also hard-crashes some embedded-camera web servers (observed on Shenzhen TVT "Cross Web Server" devices: the socket EOFs mid-response and the camera reboots), because Savon / HTTPI use httpclient for their SOAP/ONVIF calls.

This is the same case #27 noted but left unhandled ("RFC 2617 includes an example qop="auth,auth-int" though...").

The fix

  1. parse_challenge_param: quote-aware split, so qop="auth,auth-int" is kept intact instead of broken at the inner comma. Bare (unquoted) values keep working.
  2. calc_cred: the Authorization request header must carry exactly one unquoted qop-value (message-qop = "qop" "=" qop-value). Select auth when offered (auth-int additionally requires hashing the entity body), else the first option, and compute the response digest with that same value so the header and digest stay consistent.

Result:

Authorization: Digest ..., nc=00000001, qop=auth, response="<32 hex>", algorithm=MD5, opaque="..."

Tests

Adds test_digest_auth_multi_value_qop. Full test/test_auth.rb suite passes (28 tests, 71 assertions, 0 failures) on Ruby 4.0.

parse_challenge_param split the WWW-Authenticate challenge on every comma,
including the one inside a quoted qop list such as qop="auth,auth-int". That
mis-parsed qop as %{"auth} and produced a malformed request header:

  Authorization: Digest ..., qop="auth, response="...

(an unclosed qop quote that swallows the response= field). Lenient servers
ignore it; stricter ones reject the request, and some embedded camera web
servers choke on it entirely.

- parse_challenge_param: quote-aware split, so qop="auth,auth-int" is kept
  intact rather than broken at the inner comma.
- calc_cred: send exactly one unquoted qop-value (RFC 2617 message-qop),
  preferring "auth", and compute the response digest with that same value.

Adds test_digest_auth_multi_value_qop.

Fixes nahi#441
@rgaufman
rgaufman force-pushed the fix-digest-qop-multivalue-441 branch from 5b4f934 to 76ba2f3 Compare July 17, 2026 13:33
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.

Issue parsing Digest challenge

1 participant