Skip to content

Validate Host header against TLS domains in HTTP→HTTPS redirect#120

Merged
kevinmcconnell merged 3 commits intomainfrom
check-tls-domain-for-redirects
Mar 25, 2026
Merged

Validate Host header against TLS domains in HTTP→HTTPS redirect#120
kevinmcconnell merged 3 commits intomainfrom
check-tls-domain-for-redirects

Conversation

@flavorjones
Copy link
Copy Markdown
Member

@flavorjones flavorjones commented Mar 24, 2026

Summary

  • The httpRedirectHandler built the redirect Location header directly from the client-supplied Host header without checking it against the configured TLS_DOMAIN allowlist. A spoofed Host header would produce a 301 redirect to https://<spoofed-host>/....
  • The handler now validates the request Host against configured TLS domains before redirecting, returning 421 Misdirected Request for unrecognized hosts.
  • Both configured domains and incoming hosts are normalized via idna.Lookup.ToASCII to match the normalization that autocert.HostWhitelist applies for certificate issuance. Hosts or configured domains that fail IDNA normalization are rejected/skipped.

Test plan

  • Spoofed host gets 421 Misdirected Request (not 301)
  • Allowed domain gets 301 redirect with correct Location
  • Allowed domain with port gets 301 redirect (port stripped, HTTPS defaults to 443)
  • Multiple TLS domains work correctly
  • Case-insensitive domain matching (via IDNA normalization)
  • Unicode/Punycode IDN domains match correctly
  • Host failing IDNA normalization gets 421
  • Full test suite passes

ref: https://3.basecamp.com/2914079/buckets/1666/card_tables/cards/9714031628

Reported by @Pa345-ai

🤖 Generated with Claude Code

Copilot AI review requested due to automatic review settings March 24, 2026 19:57
The httpRedirectHandler built the redirect Location header directly from
the client-supplied Host header without checking it against the
configured TLS_DOMAIN allowlist. A request with a spoofed Host header
would get a 301 redirect to https://<spoofed-host>/..., which is an open
redirect (though not exploitable from a browser without MITM).

The handler now validates the request Host against the configured TLS
domains before redirecting, returning 421 Misdirected Request for
unrecognized hosts. Both configured domains and incoming hosts are
normalized via idna.Lookup.ToASCII to match the normalization that
autocert.HostWhitelist applies for certificate issuance. Hosts or
configured domains that fail IDNA normalization are rejected/skipped,
again matching autocert behavior.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@flavorjones flavorjones force-pushed the check-tls-domain-for-redirects branch from 1f3d3a1 to b090a2e Compare March 24, 2026 19:57
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR mitigates an open-redirect vector in the HTTP→HTTPS redirect path by validating the incoming Host header against the configured TLS_DOMAIN allowlist (including IDNA normalization), returning 421 Misdirected Request for unrecognized/invalid hosts.

Changes:

  • Refactors httpRedirectHandler to accept configured TLS domains, normalize them via idna.Lookup.ToASCII, and enforce allowlisted redirects.
  • Returns 421 Misdirected Request (instead of 301) for spoofed or IDNA-invalid Host headers.
  • Adds test coverage for allowlist behavior (spoofed host, ports, multiple domains, case-insensitivity, IDN handling).

Tip

If you aren't ready for review, convert to a draft PR.
Click "Convert to draft" or run gh pr ready --undo.
Click "Ready for review" or run gh pr ready to reengage.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

File Description
internal/server.go Implements allowlist + IDNA normalization for HTTP→HTTPS redirects; rejects unrecognized hosts with 421.
internal/server_test.go Adds unit tests covering spoofed host rejection and allowed host redirect behavior (including ports, multiple domains, and IDNs).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread internal/server.go Outdated
Comment thread internal/server.go Outdated
Comment thread internal/server_test.go Outdated
- Document behavior when all TLS domains fail IDNA normalization
- Downgrade per-request IDNA failure log from Warn to Debug to avoid
  log noise from client-controlled input
- Assert Location header in IDN subtests for both unicode and punycode hosts

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@Pa345-ai
Copy link
Copy Markdown

Hi — I reported a Host header injection / open redirect issue via HackerOne on March 22, which appears to align with the fix in this PR (HTTP→HTTPS redirect using unvalidated Host).

Appreciate the remediation here. If this change was informed by that report, I’d be grateful for attribution in the PR or changelog.

Thanks.

Copilot AI review requested due to automatic review settings March 25, 2026 10:29

This comment was marked as outdated.

Use the already-configured `HostPolicy` to determine whether the
requested host is in our allowed list of domains.
@kevinmcconnell kevinmcconnell force-pushed the check-tls-domain-for-redirects branch from 69d594a to 186b8f5 Compare March 25, 2026 10:33
@kevinmcconnell
Copy link
Copy Markdown
Collaborator

@flavorjones thanks for the fix! The logic looks good. I think we can lean on the existing HostPolicy to do the checking, rather than duplicate it, so I've refactored this a little to do that.

@kevinmcconnell kevinmcconnell merged commit bddb1d8 into main Mar 25, 2026
6 checks passed
@kevinmcconnell kevinmcconnell deleted the check-tls-domain-for-redirects branch March 25, 2026 12:42
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.

4 participants