Skip to content

πŸ“ [Proposal]: Host authorization middleware β€” DNS rebinding protection via Host header validationΒ #4148

@mutantkeyboard

Description

@mutantkeyboard

Feature Proposal Description

A middleware that validates the incoming Host header against a configurable allowlist, rejecting requests with unrecognized hosts with 403 Forbidden. This protects against DNS rebinding attacks, where an
attacker-controlled domain resolves to the application's internal IP, causing browsers to send requests with a malicious Host header to the application.

Alignment with Express API

Express does not have a built-in host authorization middleware. The closest third-party equivalent is host-validation -> https://github.com/brannondorsey/host-validation

HTTP RFC Standards Compliance

RFC 9112 (HTTP/1.1), Section 3.2 requires servers to respond with 400 Bad Request to requests that lack a Host header, contain multiple Host headers, or have an invalid Host value. This middleware builds on top
of that baseline requirement.

RFC 9110 (HTTP Semantics), Section 7.2 ("Host and :authority") establishes that the Host header identifies the target origin server for the request. Section 17.1 ("Establishing Authority") states:

An origin server is responsible for ensuring services controlling its certificate's private key are prepared to reject requests that appear to have been misdirected.

This middleware implements that recommendation at the application layer by rejecting requests whose Host header does not match the expected values β€” going beyond the RFC's minimum syntactic validation to
provide semantic validation against DNS rebinding and host header injection attacks.

The middleware will:

  • Strip the port from the Host header before matching (per RFC 9110 Section 7.2, the host and port are separate components)
  • Respect X-Forwarded-Host when Fiber's TrustProxy is enabled
  • Return 403 Forbidden by default for unauthorized hosts (not 400, since the request is syntactically valid but semantically unauthorized)

API Stability

The API surface is minimal and modeled after patterns already proven stable in Fiber:

  1. AllowedHosts []string β€” mirrors cors.AllowOrigins. String-based allowlists are the most stable configuration pattern.
  2. AllowedHostsFunc func(string) bool β€” mirrors cors.AllowOriginsFunc. Same escape hatch pattern.
  3. Next, Exclude, ErrorHandler β€” all existing Fiber conventions used across multiple middleware.
  4. Matching semantics (exact, subdomain wildcard, CIDR) β€” these are the same patterns Rails has used since Rails 6 (2019) without breaking changes.

No external dependencies are required β€” only net from the standard library for CIDR parsing. The middleware has no state, no caching, and no side effects beyond rejecting requests, which makes API evolution straightforward.

Feature Examples

Protect a production API


  app := fiber.New()

  app.Use(hostauthorization.New(hostauthorization.Config{
      AllowedHosts: []string{"api.myapp.com"},
  }))

  app.Get("/users", func(c fiber.Ctx) error {
      return c.JSON(getUsers())
  })

  // Host: api.myapp.com β†’ 200 OK
  // Host: evil.com      β†’ 403 Forbidden

  Allow subdomains and exclude health checks

  app.Use(hostauthorization.New(hostauthorization.Config{
      AllowedHosts: []string{".myapp.com"},
      Exclude: func(c fiber.Ctx) bool {
          return c.Path() == "/healthz"
      },
  }))

  // Host: api.myapp.com  GET /users   β†’ 200 OK
  // Host: www.myapp.com  GET /        β†’ 200 OK
  // Host: evil.com       GET /users   β†’ 403 Forbidden
  // Host: evil.com       GET /healthz β†’ 200 OK (excluded)



Internal service with CIDR ranges:


  app.Use(hostauthorization.New(hostauthorization.Config{
      AllowedHosts: []string{
          "internal.myapp.com",
          "10.0.0.0/8",       // internal network
          "127.0.0.1",        // localhost
      },
  }))

  // Host: internal.myapp.com β†’ 200 OK
  // Host: 10.0.50.3          β†’ 200 OK
  // Host: 169.254.169.254    β†’ 403 Forbidden (blocks cloud metadata)


Scoped to a route group:


  admin := app.Group("/admin")
  admin.Use(hostauthorization.New(hostauthorization.Config{
      AllowedHosts: []string{"admin.myapp.com"},
  }))

  // Host: admin.myapp.com GET /admin/dashboard β†’ 200 OK
  // Host: myapp.com       GET /admin/dashboard β†’ 403 Forbidden
  // Host: myapp.com       GET /public          β†’ 200 OK (not in group)

Checklist:

  • I agree to follow Fiber's Code of Conduct.
  • I have searched for existing issues that describe my proposal before opening this one.
  • I understand that a proposal that does not meet these guidelines may be closed without explanation.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    Status

    No status

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions