Skip to content

fix(http): reject CR/LF/NUL in set_headers values#28

Draft
andypost wants to merge 1 commit into
pre-1.35.6from
fix/set-headers-reject-crlf
Draft

fix(http): reject CR/LF/NUL in set_headers values#28
andypost wants to merge 1 commit into
pre-1.35.6from
fix/set-headers-reject-crlf

Conversation

@andypost

Copy link
Copy Markdown
Owner

Summary

The set_headers action lets operators add response headers whose
values are templated strings, e.g.

{ "response_headers": { "X-Echo": "$uri" } }

A template can dereference request-controlled state (URI, query args,
request headers). When the request URI contains %0D%0A, the
template substitution produces a value with embedded CRLF, and the
wire serialiser writes:

X-Echo: /prefix
Injected: x

— standard HTTP response splitting.

This PR rejects any computed value that contains CR, LF, or NUL. An
unsafe value is dropped (matching the existing NULL-template-result
semantics: the header is omitted from the response). An INFO line
names the offending header so an operator can diagnose a misconfigured
template without leaking the request payload at higher log levels.

Static config values are operator-controlled and conventionally
trusted, but the check is two compares per byte and applies uniformly
to both the static and templated paths.

Failing the request was considered and rejected: a 500 response would
help an attacker probe for the protection. Dropping the header keeps
the response well-formed.

Files

  • src/nxt_http_set_headers.c — adds nxt_http_header_value_is_safe
    helper and one call site after value evaluation.

Tests

./configure --debug --tests --openssl
make -j$(nproc)                                  # clean build

Manual reproduction:

{
  "listeners": { "*:8080": { "pass": "routes" } },
  "routes": [{
    "action": {
      "return": 200,
      "response_headers": { "X-Echo": "$uri" }
    }
  }]
}
$ curl -i 'http://localhost:8080/foo%0D%0AInjected:%20x'
HTTP/1.1 200 OK
... (pre-fix: contains "Injected: x" as a separate header)
... (post-fix: X-Echo omitted; INFO line in unit.log)

Independence

Single file, single concern, no dependency on other in-flight PRs.


Generated by Claude Code

The set_headers action lets operators add response headers whose values
are templated strings, e.g.

    "response_headers": { "X-Echo": "$uri" }

A template can dereference request-controlled state (URI, query
arguments, headers).  If the request URI contains "%0D%0A", the
template substitution produces a value with embedded CRLF, and the
wire serialiser writes "X-Echo: prefix\r\nInjected: x\r\n" — standard
HTTP response splitting.

Reject any computed value that contains CR, LF, or NUL.  An unsafe
value is dropped (same semantics as a NULL template result) and an
INFO line names the header so an operator can diagnose a misconfigured
template without exposing the offending request payload at higher log
levels.

Static config values are operator-controlled and conventionally
trusted, but the check is two compares per byte and applies uniformly
to both code paths.

Failing the request would help an attacker probe for the protection;
silently dropping the header keeps the response well-formed.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a safety check nxt_http_header_value_is_safe to prevent HTTP response-splitting vulnerabilities. It scans header values for carriage return (CR), line feed (LF), or null (NUL) characters, and drops any unsafe values while logging an info message. There are no review comments, and I have no feedback to provide.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

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