Skip to content

fix: guard read_body against HTTP/2 reload crash in captcha verify state#142

Open
drlogout wants to merge 1 commit into
crowdsecurity:mainfrom
drlogout:fix-http2-read-body-verify-state
Open

fix: guard read_body against HTTP/2 reload crash in captcha verify state#142
drlogout wants to merge 1 commit into
crowdsecurity:mainfrom
drlogout:fix-http2-read-body-verify-state

Conversation

@drlogout
Copy link
Copy Markdown

Summary

Fixes a runtime crash when a client with a captcha-type decision reloads the
challenge page over HTTP/2. Closes #63.

Problem

ngx.req.read_body() raises an error for HTTP/2 (and HTTP/3) requests without
a Content-Length header. The body-read inside csmod.Allow()'s captcha
verify-state handler is unguarded, so any HTTP/2 GET reload of the captcha
page crashes nginx with HTTP 500:

lua entry thread aborted: runtime error:
.../crowdsec.lua:NNN: http2 requests are not supported without content-length header
stack traceback:
[C]: in function 'read_body'
.../crowdsec.lua:NNN: in function 'Allow'

The get_body() helper already implements the correct guard; this PR applies
the same pattern to the second read_body call site.

Reproduction

  1. Deploy crowdsec-nginx-bouncer with a captcha provider configured (e.g. Turnstile, hCaptcha)
  2. Serve a site over HTTP/2 (default for HTTPS on modern nginx)
  3. Force a captcha decision: cscli decisions add -i <your-ip> -t captcha -d 10m
  4. Visit the site in a browser → captcha page renders (first request, state cache empty)
  5. Reload the page → HTTP 500 + read_body error in nginx error log

Fix

Mirror the HTTP/2 protocol-version guard from get_body(). When the body is
unreadable, skip the read and fall through with an empty args table, which
correctly re-serves the captcha page. Captcha form submissions are POSTs
with Content-Length, so they're unaffected.

Compatibility

  • HTTP/1.1: behavior unchanged
  • HTTP/2 GET (e.g. reload of captcha page): no longer crashes; re-serves captcha
  • HTTP/2 POST with Content-Length (captcha form submission): behavior unchanged
  • HTTP/2 POST without Content-Length: body skipped (same as get_body behavior)

Tested

  • Reproduced the crash on a production deployment of crowdsec-nginx-bouncer v1.1.6
  • Applied the patch and verified:
  • First captcha load renders normally
  • Reload of the captcha page no longer crashes
  • Captcha form submission validates and unblocks the IP as expected
  • Verified no regression in HTTP/1.1 flow
    EOF
    )"

ngx.req.read_body() raises a runtime error for HTTP/2 (and HTTP/3) requests
without a Content-Length header. The captcha verify-state handler in
csmod.Allow() called read_body unconditionally, causing nginx to return
HTTP 500 whenever a client with an active captcha decision reloaded the
challenge page over HTTP/2.

The guard mirrors the one already used in get_body(): if the protocol is
HTTP/2+ and Content-Length is missing, skip the body read and treat the
request as having no POST args. Genuine captcha form submissions are
POSTs from browsers that always include Content-Length, so the guard
does not interfere with the verification flow.

Closes crowdsecurity#63
@drlogout drlogout changed the title fix: guard read_body in captcha verify state for HTTP/2 requests fix: guard read_body against HTTP/2 reload crash in captcha verify state May 24, 2026
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.

HTTP2/3 Check request before invoking read_body

1 participant