🔥Feature: Add support for TrustProxy (#3170)

* 🔥 Feature: Add `TrustProxyConfig` and rename `EnableTrustedProxyCheck` to `TrustProxy`

* 📚 Doc: Document TrustProxyConfig usage and migration

* 🚨 Test: Validate and Benchmark use of TrustProxyConfig

* 🩹 Fix: typo in RequestMethods docstring

* 🩹 Fix: typos in TrustProxy docstring and JSON tags

* 🩹 Fix: Move `TrustProxyConfig.Loopback` to beginning of if-statement

* 🎨 Style: Cleanup spacing for Test_Ctx_IsProxyTrusted

* 📚 Doc: Replace `whitelist` with `allowlist` for clarity

* 📚 Doc: Improve `TrustProxy` doc wording

* 🩹 Fix: validate IP addresses in `App.handleTrustedProxy`

* 🩹 Fix: grammatical errors and capitalize "TLS"
pull/3174/head
xEricL 2024-10-17 02:29:03 -04:00 committed by GitHub
parent 7b3a36f22f
commit 298975a982
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 518 additions and 175 deletions

6
.github/README.md vendored
View File

@ -561,8 +561,10 @@ import (
func main() {
app := fiber.New(fiber.Config{
EnableTrustedProxyCheck: true,
TrustedProxies: []string{"0.0.0.0", "1.1.1.1/30"}, // IP address or IP address range
TrustProxy: true,
TrustProxyConfig: fiber.TrustProxyConfig{
Proxies: []string{"0.0.0.0", "1.1.1.1/30"}, // IP address or IP address range
},
ProxyHeader: fiber.HeaderXForwardedFor,
})

77
app.go
View File

@ -330,29 +330,31 @@ type Config struct { //nolint:govet // Aligning the struct fields is not necessa
// For example, the Host HTTP header is usually used to return the requested host.
// But when youre behind a proxy, the actual host may be stored in an X-Forwarded-Host header.
//
// If you are behind a proxy, you should enable TrustedProxyCheck to prevent header spoofing.
// If you enable EnableTrustedProxyCheck and leave TrustedProxies empty Fiber will skip
// If you are behind a proxy, you should enable TrustProxy to prevent header spoofing.
// If you enable TrustProxy and do not provide a TrustProxyConfig, Fiber will skip
// all headers that could be spoofed.
// If request ip in TrustedProxies whitelist then:
// If the request IP is in the TrustProxyConfig.Proxies allowlist, then:
// 1. c.Scheme() get value from X-Forwarded-Proto, X-Forwarded-Protocol, X-Forwarded-Ssl or X-Url-Scheme header
// 2. c.IP() get value from ProxyHeader header.
// 3. c.Host() and c.Hostname() get value from X-Forwarded-Host header
// But if request ip NOT in Trusted Proxies whitelist then:
// 1. c.Scheme() WON't get value from X-Forwarded-Proto, X-Forwarded-Protocol, X-Forwarded-Ssl or X-Url-Scheme header,
// will return https in case when tls connection is handled by the app, of http otherwise
// But if the request IP is NOT in the TrustProxyConfig.Proxies allowlist, then:
// 1. c.Scheme() WON'T get value from X-Forwarded-Proto, X-Forwarded-Protocol, X-Forwarded-Ssl or X-Url-Scheme header,
// will return https when a TLS connection is handled by the app, or http otherwise.
// 2. c.IP() WON'T get value from ProxyHeader header, will return RemoteIP() from fasthttp context
// 3. c.Host() and c.Hostname() WON'T get value from X-Forwarded-Host header, fasthttp.Request.URI().Host()
// will be used to get the hostname.
//
// Default: false
EnableTrustedProxyCheck bool `json:"enable_trusted_proxy_check"`
// Read EnableTrustedProxyCheck doc.
// To automatically trust all loopback, link-local, or private IP addresses,
// without manually adding them to the TrustProxyConfig.Proxies allowlist,
// you can set TrustProxyConfig.Loopback, TrustProxyConfig.LinkLocal, or TrustProxyConfig.Private to true.
//
// Default: []string
TrustedProxies []string `json:"trusted_proxies"`
trustedProxiesMap map[string]struct{}
trustedProxyRanges []*net.IPNet
// Default: false
TrustProxy bool `json:"trust_proxy"`
// Read TrustProxy doc.
//
// Default: DefaultTrustProxyConfig
TrustProxyConfig TrustProxyConfig `json:"trust_proxy_config"`
// If set to true, c.IP() and c.IPs() will validate IP addresses before returning them.
// Also, c.IP() will return only the first valid IP rather than just the raw header
@ -372,7 +374,7 @@ type Config struct { //nolint:govet // Aligning the struct fields is not necessa
// Default: nil
StructValidator StructValidator
// RequestMethods provides customizibility for HTTP methods. You can add/remove methods as you wish.
// RequestMethods provides customizability for HTTP methods. You can add/remove methods as you wish.
//
// Optional. Default: DefaultMethods
RequestMethods []string
@ -385,6 +387,36 @@ type Config struct { //nolint:govet // Aligning the struct fields is not necessa
EnableSplittingOnParsers bool `json:"enable_splitting_on_parsers"`
}
// Default TrustProxyConfig
var DefaultTrustProxyConfig = TrustProxyConfig{}
// TrustProxyConfig is a struct for configuring trusted proxies if Config.TrustProxy is true.
type TrustProxyConfig struct {
ips map[string]struct{}
// Proxies is a list of trusted proxy IP addresses or CIDR ranges.
//
// Default: []string
Proxies []string `json:"proxies"`
ranges []*net.IPNet
// LinkLocal enables trusting all link-local IP ranges (e.g., 169.254.0.0/16, fe80::/10).
//
// Default: false
LinkLocal bool `json:"link_local"`
// Loopback enables trusting all loopback IP ranges (e.g., 127.0.0.0/8, ::1/128).
//
// Default: false
Loopback bool `json:"loopback"`
// Private enables trusting all private IP ranges (e.g., 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, fc00::/7).
//
// Default: false
Private bool `json:"private"`
}
// RouteMessage is some message need to be print when server starts
type RouteMessage struct {
name string
@ -510,8 +542,8 @@ func New(config ...Config) *App {
app.config.RequestMethods = DefaultMethods
}
app.config.trustedProxiesMap = make(map[string]struct{}, len(app.config.TrustedProxies))
for _, ipAddress := range app.config.TrustedProxies {
app.config.TrustProxyConfig.ips = make(map[string]struct{}, len(app.config.TrustProxyConfig.Proxies))
for _, ipAddress := range app.config.TrustProxyConfig.Proxies {
app.handleTrustedProxy(ipAddress)
}
@ -529,17 +561,22 @@ func New(config ...Config) *App {
return app
}
// Adds an ip address to trustedProxyRanges or trustedProxiesMap based on whether it is an IP range or not
// Adds an ip address to TrustProxyConfig.ranges or TrustProxyConfig.ips based on whether it is an IP range or not
func (app *App) handleTrustedProxy(ipAddress string) {
if strings.Contains(ipAddress, "/") {
_, ipNet, err := net.ParseCIDR(ipAddress)
if err != nil {
log.Warnf("IP range %q could not be parsed: %v", ipAddress, err)
} else {
app.config.trustedProxyRanges = append(app.config.trustedProxyRanges, ipNet)
app.config.TrustProxyConfig.ranges = append(app.config.TrustProxyConfig.ranges, ipNet)
}
} else {
app.config.trustedProxiesMap[ipAddress] = struct{}{}
ip := net.ParseIP(ipAddress)
if ip == nil {
log.Warnf("IP address %q could not be parsed", ipAddress)
} else {
app.config.TrustProxyConfig.ips[ipAddress] = struct{}{}
}
}
}

24
ctx.go
View File

@ -155,7 +155,7 @@ type TLSHandler struct {
// GetClientInfo Callback function to set ClientHelloInfo
// Must comply with the method structure of https://cs.opensource.google/go/go/+/refs/tags/go1.20:src/crypto/tls/common.go;l=554-563
// Since we overlay the method of the tls config in the listener method
// Since we overlay the method of the TLS config in the listener method
func (t *TLSHandler) GetClientInfo(info *tls.ClientHelloInfo) (*tls.Certificate, error) {
t.clientHelloInfo = info
return nil, nil //nolint:nilnil // Not returning anything useful here is probably fine
@ -684,7 +684,7 @@ func (c *DefaultCtx) GetReqHeaders() map[string][]string {
// while `Hostname` refers specifically to the name assigned to a device on a network, excluding any port information.
// Example: URL: https://example.com:8080 -> Host: example.com:8080
// Make copies or use the Immutable setting instead.
// Please use Config.EnableTrustedProxyCheck to prevent header spoofing, in case when your app is behind the proxy.
// Please use Config.TrustProxy to prevent header spoofing, in case when your app is behind the proxy.
func (c *DefaultCtx) Host() string {
if c.IsProxyTrusted() {
if host := c.Get(HeaderXForwardedHost); len(host) > 0 {
@ -702,7 +702,7 @@ func (c *DefaultCtx) Host() string {
// Returned value is only valid within the handler. Do not store any references.
// Example: URL: https://example.com:8080 -> Hostname: example.com
// Make copies or use the Immutable setting instead.
// Please use Config.EnableTrustedProxyCheck to prevent header spoofing, in case when your app is behind the proxy.
// Please use Config.TrustProxy to prevent header spoofing, in case when your app is behind the proxy.
func (c *DefaultCtx) Hostname() string {
addr, _ := parseAddr(c.Host())
@ -720,7 +720,7 @@ func (c *DefaultCtx) Port() string {
// IP returns the remote IP address of the request.
// If ProxyHeader and IP Validation is configured, it will parse that header and return the first valid IP address.
// Please use Config.EnableTrustedProxyCheck to prevent header spoofing, in case when your app is behind the proxy.
// Please use Config.TrustProxy to prevent header spoofing, in case when your app is behind the proxy.
func (c *DefaultCtx) IP() string {
if c.IsProxyTrusted() && len(c.app.config.ProxyHeader) > 0 {
return c.extractIPFromHeader(c.app.config.ProxyHeader)
@ -1116,7 +1116,7 @@ func (c *DefaultCtx) Path(override ...string) string {
}
// Scheme contains the request protocol string: http or https for TLS requests.
// Please use Config.EnableTrustedProxyCheck to prevent header spoofing, in case when your app is behind the proxy.
// Please use Config.TrustProxy to prevent header spoofing, in case when your app is behind the proxy.
func (c *DefaultCtx) Scheme() string {
if c.fasthttp.IsTLS() {
return schemeHTTPS
@ -1819,20 +1819,26 @@ func (c *DefaultCtx) configDependentPaths() {
}
// IsProxyTrusted checks trustworthiness of remote ip.
// If EnableTrustedProxyCheck false, it returns true
// If Config.TrustProxy false, it returns true
// IsProxyTrusted can check remote ip by proxy ranges and ip map.
func (c *DefaultCtx) IsProxyTrusted() bool {
if !c.app.config.EnableTrustedProxyCheck {
if !c.app.config.TrustProxy {
return true
}
ip := c.fasthttp.RemoteIP()
if _, trusted := c.app.config.trustedProxiesMap[ip.String()]; trusted {
if (c.app.config.TrustProxyConfig.Loopback && ip.IsLoopback()) ||
(c.app.config.TrustProxyConfig.Private && ip.IsPrivate()) ||
(c.app.config.TrustProxyConfig.LinkLocal && ip.IsLinkLocalUnicast()) {
return true
}
for _, ipNet := range c.app.config.trustedProxyRanges {
if _, trusted := c.app.config.TrustProxyConfig.ips[ip.String()]; trusted {
return true
}
for _, ipNet := range c.app.config.TrustProxyConfig.ranges {
if ipNet.Contains(ip) {
return true
}

View File

@ -127,19 +127,19 @@ type Ctx interface {
// while `Hostname` refers specifically to the name assigned to a device on a network, excluding any port information.
// Example: URL: https://example.com:8080 -> Host: example.com:8080
// Make copies or use the Immutable setting instead.
// Please use Config.EnableTrustedProxyCheck to prevent header spoofing, in case when your app is behind the proxy.
// Please use Config.TrustProxy to prevent header spoofing, in case when your app is behind the proxy.
Host() string
// Hostname contains the hostname derived from the X-Forwarded-Host or Host HTTP header using the c.Host() method.
// Returned value is only valid within the handler. Do not store any references.
// Example: URL: https://example.com:8080 -> Hostname: example.com
// Make copies or use the Immutable setting instead.
// Please use Config.EnableTrustedProxyCheck to prevent header spoofing, in case when your app is behind the proxy.
// Please use Config.TrustProxy to prevent header spoofing, in case when your app is behind the proxy.
Hostname() string
// Port returns the remote port of the request.
Port() string
// IP returns the remote IP address of the request.
// If ProxyHeader and IP Validation is configured, it will parse that header and return the first valid IP address.
// Please use Config.EnableTrustedProxyCheck to prevent header spoofing, in case when your app is behind the proxy.
// Please use Config.TrustProxy to prevent header spoofing, in case when your app is behind the proxy.
IP() string
// extractIPsFromHeader will return a slice of IPs it found given a header name in the order they appear.
// When IP validation is enabled, any invalid IPs will be omitted.
@ -209,7 +209,7 @@ type Ctx interface {
// Optionally, you could override the path.
Path(override ...string) string
// Scheme contains the request protocol string: http or https for TLS requests.
// Please use Config.EnableTrustedProxyCheck to prevent header spoofing, in case when your app is behind the proxy.
// Please use Config.TrustProxy to prevent header spoofing, in case when your app is behind the proxy.
Scheme() string
// Protocol returns the HTTP protocol of request: HTTP/1.1 and HTTP/2.
Protocol() string
@ -315,7 +315,7 @@ type Ctx interface {
// here the features for caseSensitive, decoded paths, strict paths are evaluated
configDependentPaths()
// IsProxyTrusted checks trustworthiness of remote ip.
// If EnableTrustedProxyCheck false, it returns true
// If Config.TrustProxy false, it returns true
// IsProxyTrusted can check remote ip by proxy ranges and ip map.
IsProxyTrusted() bool
// IsFromLocal will return true if request came from local.

View File

@ -1572,7 +1572,7 @@ func Test_Ctx_Host_UntrustedProxy(t *testing.T) {
t.Parallel()
// Don't trust any proxy
{
app := New(Config{EnableTrustedProxyCheck: true, TrustedProxies: []string{}})
app := New(Config{TrustProxy: true, TrustProxyConfig: TrustProxyConfig{Proxies: []string{}}})
c := app.AcquireCtx(&fasthttp.RequestCtx{})
c.Request().SetRequestURI("http://google.com/test")
c.Request().Header.Set(HeaderXForwardedHost, "google1.com")
@ -1581,7 +1581,7 @@ func Test_Ctx_Host_UntrustedProxy(t *testing.T) {
}
// Trust to specific proxy list
{
app := New(Config{EnableTrustedProxyCheck: true, TrustedProxies: []string{"0.8.0.0", "0.8.0.1"}})
app := New(Config{TrustProxy: true, TrustProxyConfig: TrustProxyConfig{Proxies: []string{"0.8.0.0", "0.8.0.1"}}})
c := app.AcquireCtx(&fasthttp.RequestCtx{})
c.Request().SetRequestURI("http://google.com/test")
c.Request().Header.Set(HeaderXForwardedHost, "google1.com")
@ -1594,7 +1594,7 @@ func Test_Ctx_Host_UntrustedProxy(t *testing.T) {
func Test_Ctx_Host_TrustedProxy(t *testing.T) {
t.Parallel()
{
app := New(Config{EnableTrustedProxyCheck: true, TrustedProxies: []string{"0.0.0.0", "0.8.0.1"}})
app := New(Config{TrustProxy: true, TrustProxyConfig: TrustProxyConfig{Proxies: []string{"0.0.0.0", "0.8.0.1"}}})
c := app.AcquireCtx(&fasthttp.RequestCtx{})
c.Request().SetRequestURI("http://google.com/test")
c.Request().Header.Set(HeaderXForwardedHost, "google1.com")
@ -1607,7 +1607,7 @@ func Test_Ctx_Host_TrustedProxy(t *testing.T) {
func Test_Ctx_Host_TrustedProxyRange(t *testing.T) {
t.Parallel()
app := New(Config{EnableTrustedProxyCheck: true, TrustedProxies: []string{"0.0.0.0/30"}})
app := New(Config{TrustProxy: true, TrustProxyConfig: TrustProxyConfig{Proxies: []string{"0.0.0.0/30"}}})
c := app.AcquireCtx(&fasthttp.RequestCtx{})
c.Request().SetRequestURI("http://google.com/test")
c.Request().Header.Set(HeaderXForwardedHost, "google1.com")
@ -1619,7 +1619,7 @@ func Test_Ctx_Host_TrustedProxyRange(t *testing.T) {
func Test_Ctx_Host_UntrustedProxyRange(t *testing.T) {
t.Parallel()
app := New(Config{EnableTrustedProxyCheck: true, TrustedProxies: []string{"1.0.0.0/30"}})
app := New(Config{TrustProxy: true, TrustProxyConfig: TrustProxyConfig{Proxies: []string{"1.0.0.0/30"}}})
c := app.AcquireCtx(&fasthttp.RequestCtx{})
c.Request().SetRequestURI("http://google.com/test")
c.Request().Header.Set(HeaderXForwardedHost, "google1.com")
@ -1653,7 +1653,7 @@ func Test_Ctx_IsProxyTrusted(t *testing.T) {
}
{
app := New(Config{
EnableTrustedProxyCheck: false,
TrustProxy: false,
})
c := app.AcquireCtx(&fasthttp.RequestCtx{})
require.True(t, c.IsProxyTrusted())
@ -1661,26 +1661,16 @@ func Test_Ctx_IsProxyTrusted(t *testing.T) {
{
app := New(Config{
EnableTrustedProxyCheck: true,
TrustProxy: true,
})
c := app.AcquireCtx(&fasthttp.RequestCtx{})
require.False(t, c.IsProxyTrusted())
}
{
app := New(Config{
EnableTrustedProxyCheck: true,
TrustedProxies: []string{},
})
c := app.AcquireCtx(&fasthttp.RequestCtx{})
require.False(t, c.IsProxyTrusted())
}
{
app := New(Config{
EnableTrustedProxyCheck: true,
TrustedProxies: []string{
"127.0.0.1",
TrustProxy: true,
TrustProxyConfig: TrustProxyConfig{
Proxies: []string{},
},
})
c := app.AcquireCtx(&fasthttp.RequestCtx{})
@ -1688,10 +1678,9 @@ func Test_Ctx_IsProxyTrusted(t *testing.T) {
}
{
app := New(Config{
EnableTrustedProxyCheck: true,
TrustedProxies: []string{
"127.0.0.1/8",
TrustProxy: true,
TrustProxyConfig: TrustProxyConfig{
Proxies: []string{"127.0.0.1"},
},
})
c := app.AcquireCtx(&fasthttp.RequestCtx{})
@ -1699,10 +1688,19 @@ func Test_Ctx_IsProxyTrusted(t *testing.T) {
}
{
app := New(Config{
EnableTrustedProxyCheck: true,
TrustedProxies: []string{
"0.0.0.0",
TrustProxy: true,
TrustProxyConfig: TrustProxyConfig{
Proxies: []string{"127.0.0.1/8"},
},
})
c := app.AcquireCtx(&fasthttp.RequestCtx{})
require.False(t, c.IsProxyTrusted())
}
{
app := New(Config{
TrustProxy: true,
TrustProxyConfig: TrustProxyConfig{
Proxies: []string{"0.0.0.0"},
},
})
c := app.AcquireCtx(&fasthttp.RequestCtx{})
@ -1710,10 +1708,9 @@ func Test_Ctx_IsProxyTrusted(t *testing.T) {
}
{
app := New(Config{
EnableTrustedProxyCheck: true,
TrustedProxies: []string{
"0.0.0.1/31",
TrustProxy: true,
TrustProxyConfig: TrustProxyConfig{
Proxies: []string{"0.0.0.1/31"},
},
})
c := app.AcquireCtx(&fasthttp.RequestCtx{})
@ -1721,10 +1718,39 @@ func Test_Ctx_IsProxyTrusted(t *testing.T) {
}
{
app := New(Config{
EnableTrustedProxyCheck: true,
TrustedProxies: []string{
"0.0.0.1/31junk",
TrustProxy: true,
TrustProxyConfig: TrustProxyConfig{
Proxies: []string{"0.0.0.1/31junk"},
},
})
c := app.AcquireCtx(&fasthttp.RequestCtx{})
require.False(t, c.IsProxyTrusted())
}
{
app := New(Config{
TrustProxy: true,
TrustProxyConfig: TrustProxyConfig{
Private: true,
},
})
c := app.AcquireCtx(&fasthttp.RequestCtx{})
require.False(t, c.IsProxyTrusted())
}
{
app := New(Config{
TrustProxy: true,
TrustProxyConfig: TrustProxyConfig{
Loopback: true,
},
})
c := app.AcquireCtx(&fasthttp.RequestCtx{})
require.False(t, c.IsProxyTrusted())
}
{
app := New(Config{
TrustProxy: true,
TrustProxyConfig: TrustProxyConfig{
LinkLocal: true,
},
})
c := app.AcquireCtx(&fasthttp.RequestCtx{})
@ -1758,7 +1784,10 @@ func Benchmark_Ctx_Hostname(b *testing.B) {
}
// Trust to specific proxy list
{
app := New(Config{EnableTrustedProxyCheck: true, TrustedProxies: []string{"0.8.0.0", "0.8.0.1"}})
app := New(Config{
TrustProxy: true,
TrustProxyConfig: TrustProxyConfig{Proxies: []string{"0.8.0.0", "0.8.0.1"}},
})
c := app.AcquireCtx(&fasthttp.RequestCtx{})
c.Request().SetRequestURI("http://google.com/test")
c.Request().Header.Set(HeaderXForwardedHost, "google1.com")
@ -1771,7 +1800,10 @@ func Benchmark_Ctx_Hostname(b *testing.B) {
func Test_Ctx_Hostname_TrustedProxy(t *testing.T) {
t.Parallel()
{
app := New(Config{EnableTrustedProxyCheck: true, TrustedProxies: []string{"0.0.0.0", "0.8.0.1"}})
app := New(Config{
TrustProxy: true,
TrustProxyConfig: TrustProxyConfig{Proxies: []string{"0.0.0.0", "0.8.0.1"}},
})
c := app.AcquireCtx(&fasthttp.RequestCtx{})
c.Request().SetRequestURI("http://google.com/test")
c.Request().Header.Set(HeaderXForwardedHost, "google1.com")
@ -1784,7 +1816,10 @@ func Test_Ctx_Hostname_TrustedProxy(t *testing.T) {
func Test_Ctx_Hostname_TrustedProxy_Multiple(t *testing.T) {
t.Parallel()
{
app := New(Config{EnableTrustedProxyCheck: true, TrustedProxies: []string{"0.0.0.0", "0.8.0.1"}})
app := New(Config{
TrustProxy: true,
TrustProxyConfig: TrustProxyConfig{Proxies: []string{"0.0.0.0", "0.8.0.1"}},
})
c := app.AcquireCtx(&fasthttp.RequestCtx{})
c.Request().SetRequestURI("http://google.com/test")
c.Request().Header.Set(HeaderXForwardedHost, "google1.com, google2.com")
@ -1797,7 +1832,10 @@ func Test_Ctx_Hostname_TrustedProxy_Multiple(t *testing.T) {
func Test_Ctx_Hostname_TrustedProxyRange(t *testing.T) {
t.Parallel()
app := New(Config{EnableTrustedProxyCheck: true, TrustedProxies: []string{"0.0.0.0/30"}})
app := New(Config{
TrustProxy: true,
TrustProxyConfig: TrustProxyConfig{Proxies: []string{"0.0.0.0/30"}},
})
c := app.AcquireCtx(&fasthttp.RequestCtx{})
c.Request().SetRequestURI("http://google.com/test")
c.Request().Header.Set(HeaderXForwardedHost, "google1.com")
@ -1809,7 +1847,10 @@ func Test_Ctx_Hostname_TrustedProxyRange(t *testing.T) {
func Test_Ctx_Hostname_UntrustedProxyRange(t *testing.T) {
t.Parallel()
app := New(Config{EnableTrustedProxyCheck: true, TrustedProxies: []string{"1.0.0.0/30"}})
app := New(Config{
TrustProxy: true,
TrustProxyConfig: TrustProxyConfig{Proxies: []string{"1.0.0.0/30"}},
})
c := app.AcquireCtx(&fasthttp.RequestCtx{})
c.Request().SetRequestURI("http://google.com/test")
c.Request().Header.Set(HeaderXForwardedHost, "google1.com")
@ -1927,7 +1968,11 @@ func Test_Ctx_IP_ProxyHeader_With_IP_Validation(t *testing.T) {
// go test -run Test_Ctx_IP_UntrustedProxy
func Test_Ctx_IP_UntrustedProxy(t *testing.T) {
t.Parallel()
app := New(Config{EnableTrustedProxyCheck: true, TrustedProxies: []string{"0.8.0.1"}, ProxyHeader: HeaderXForwardedFor})
app := New(Config{
TrustProxy: true,
TrustProxyConfig: TrustProxyConfig{Proxies: []string{"0.8.0.1"}},
ProxyHeader: HeaderXForwardedFor,
})
c := app.AcquireCtx(&fasthttp.RequestCtx{})
c.Request().Header.Set(HeaderXForwardedFor, "0.0.0.1")
require.Equal(t, "0.0.0.0", c.IP())
@ -1936,7 +1981,11 @@ func Test_Ctx_IP_UntrustedProxy(t *testing.T) {
// go test -run Test_Ctx_IP_TrustedProxy
func Test_Ctx_IP_TrustedProxy(t *testing.T) {
t.Parallel()
app := New(Config{EnableTrustedProxyCheck: true, TrustedProxies: []string{"0.0.0.0"}, ProxyHeader: HeaderXForwardedFor})
app := New(Config{
TrustProxy: true,
TrustProxyConfig: TrustProxyConfig{Proxies: []string{"0.0.0.0"}},
ProxyHeader: HeaderXForwardedFor,
})
c := app.AcquireCtx(&fasthttp.RequestCtx{})
c.Request().Header.Set(HeaderXForwardedFor, "0.0.0.1")
require.Equal(t, "0.0.0.1", c.IP())
@ -2613,7 +2662,7 @@ func Benchmark_Ctx_Scheme(b *testing.B) {
// go test -run Test_Ctx_Scheme_TrustedProxy
func Test_Ctx_Scheme_TrustedProxy(t *testing.T) {
t.Parallel()
app := New(Config{EnableTrustedProxyCheck: true, TrustedProxies: []string{"0.0.0.0"}})
app := New(Config{TrustProxy: true, TrustProxyConfig: TrustProxyConfig{Proxies: []string{"0.0.0.0"}}})
c := app.AcquireCtx(&fasthttp.RequestCtx{})
c.Request().Header.Set(HeaderXForwardedProto, schemeHTTPS)
@ -2638,7 +2687,10 @@ func Test_Ctx_Scheme_TrustedProxy(t *testing.T) {
// go test -run Test_Ctx_Scheme_TrustedProxyRange
func Test_Ctx_Scheme_TrustedProxyRange(t *testing.T) {
t.Parallel()
app := New(Config{EnableTrustedProxyCheck: true, TrustedProxies: []string{"0.0.0.0/30"}})
app := New(Config{
TrustProxy: true,
TrustProxyConfig: TrustProxyConfig{Proxies: []string{"0.0.0.0/30"}},
})
c := app.AcquireCtx(&fasthttp.RequestCtx{})
c.Request().Header.Set(HeaderXForwardedProto, schemeHTTPS)
@ -2663,7 +2715,10 @@ func Test_Ctx_Scheme_TrustedProxyRange(t *testing.T) {
// go test -run Test_Ctx_Scheme_UntrustedProxyRange
func Test_Ctx_Scheme_UntrustedProxyRange(t *testing.T) {
t.Parallel()
app := New(Config{EnableTrustedProxyCheck: true, TrustedProxies: []string{"1.1.1.1/30"}})
app := New(Config{
TrustProxy: true,
TrustProxyConfig: TrustProxyConfig{Proxies: []string{"1.1.1.1/30"}},
})
c := app.AcquireCtx(&fasthttp.RequestCtx{})
c.Request().Header.Set(HeaderXForwardedProto, schemeHTTPS)
@ -2688,7 +2743,10 @@ func Test_Ctx_Scheme_UntrustedProxyRange(t *testing.T) {
// go test -run Test_Ctx_Scheme_UnTrustedProxy
func Test_Ctx_Scheme_UnTrustedProxy(t *testing.T) {
t.Parallel()
app := New(Config{EnableTrustedProxyCheck: true, TrustedProxies: []string{"0.8.0.1"}})
app := New(Config{
TrustProxy: true,
TrustProxyConfig: TrustProxyConfig{Proxies: []string{"0.8.0.1"}},
})
c := app.AcquireCtx(&fasthttp.RequestCtx{})
c.Request().Header.Set(HeaderXForwardedProto, schemeHTTPS)
@ -6173,7 +6231,7 @@ func Benchmark_Ctx_IsProxyTrusted(b *testing.B) {
// Scenario with trusted proxy check simple
b.Run("WithProxyCheckSimple", func(b *testing.B) {
app := New(Config{
EnableTrustedProxyCheck: true,
TrustProxy: true,
})
c := app.AcquireCtx(&fasthttp.RequestCtx{})
c.Request().SetRequestURI("http://google.com/test")
@ -6189,7 +6247,7 @@ func Benchmark_Ctx_IsProxyTrusted(b *testing.B) {
// Scenario with trusted proxy check simple in parallel
b.Run("WithProxyCheckSimpleParallel", func(b *testing.B) {
app := New(Config{
EnableTrustedProxyCheck: true,
TrustProxy: true,
})
b.ReportAllocs()
b.ResetTimer()
@ -6207,8 +6265,10 @@ func Benchmark_Ctx_IsProxyTrusted(b *testing.B) {
// Scenario with trusted proxy check
b.Run("WithProxyCheck", func(b *testing.B) {
app := New(Config{
EnableTrustedProxyCheck: true,
TrustedProxies: []string{"0.0.0.0"},
TrustProxy: true,
TrustProxyConfig: TrustProxyConfig{
Proxies: []string{"0.0.0.0"},
},
})
c := app.AcquireCtx(&fasthttp.RequestCtx{})
c.Request().SetRequestURI("http://google.com/test")
@ -6224,8 +6284,198 @@ func Benchmark_Ctx_IsProxyTrusted(b *testing.B) {
// Scenario with trusted proxy check in parallel
b.Run("WithProxyCheckParallel", func(b *testing.B) {
app := New(Config{
EnableTrustedProxyCheck: true,
TrustedProxies: []string{"0.0.0.0"},
TrustProxy: true,
TrustProxyConfig: TrustProxyConfig{
Proxies: []string{"0.0.0.0"},
},
})
b.ReportAllocs()
b.ResetTimer()
b.RunParallel(func(pb *testing.PB) {
c := app.AcquireCtx(&fasthttp.RequestCtx{})
c.Request().SetRequestURI("http://google.com/")
c.Request().Header.Set(HeaderXForwardedHost, "google1.com")
for pb.Next() {
c.IsProxyTrusted()
}
app.ReleaseCtx(c)
})
})
// Scenario with trusted proxy check allow private
b.Run("WithProxyCheckAllowPrivate", func(b *testing.B) {
app := New(Config{
TrustProxy: true,
TrustProxyConfig: TrustProxyConfig{
Private: true,
},
})
c := app.AcquireCtx(&fasthttp.RequestCtx{})
c.Request().SetRequestURI("http://google.com/test")
c.Request().Header.Set(HeaderXForwardedHost, "google1.com")
b.ReportAllocs()
b.ResetTimer()
for n := 0; n < b.N; n++ {
c.IsProxyTrusted()
}
app.ReleaseCtx(c)
})
// Scenario with trusted proxy check allow private in parallel
b.Run("WithProxyCheckAllowPrivateParallel", func(b *testing.B) {
app := New(Config{
TrustProxy: true,
TrustProxyConfig: TrustProxyConfig{
Private: true,
},
})
b.ReportAllocs()
b.ResetTimer()
b.RunParallel(func(pb *testing.PB) {
c := app.AcquireCtx(&fasthttp.RequestCtx{})
c.Request().SetRequestURI("http://google.com/")
c.Request().Header.Set(HeaderXForwardedHost, "google1.com")
for pb.Next() {
c.IsProxyTrusted()
}
app.ReleaseCtx(c)
})
})
// Scenario with trusted proxy check allow private as subnets
b.Run("WithProxyCheckAllowPrivateAsSubnets", func(b *testing.B) {
app := New(Config{
TrustProxy: true,
TrustProxyConfig: TrustProxyConfig{
Proxies: []string{"10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16", "fc00::/7"},
},
})
c := app.AcquireCtx(&fasthttp.RequestCtx{})
c.Request().SetRequestURI("http://google.com/test")
c.Request().Header.Set(HeaderXForwardedHost, "google1.com")
b.ReportAllocs()
b.ResetTimer()
for n := 0; n < b.N; n++ {
c.IsProxyTrusted()
}
app.ReleaseCtx(c)
})
// Scenario with trusted proxy check allow private as subnets in parallel
b.Run("WithProxyCheckAllowPrivateAsSubnetsParallel", func(b *testing.B) {
app := New(Config{
TrustProxy: true,
TrustProxyConfig: TrustProxyConfig{
Proxies: []string{"10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16", "fc00::/7"},
},
})
b.ReportAllocs()
b.ResetTimer()
b.RunParallel(func(pb *testing.PB) {
c := app.AcquireCtx(&fasthttp.RequestCtx{})
c.Request().SetRequestURI("http://google.com/")
c.Request().Header.Set(HeaderXForwardedHost, "google1.com")
for pb.Next() {
c.IsProxyTrusted()
}
app.ReleaseCtx(c)
})
})
// Scenario with trusted proxy check allow private, loopback, and link-local
b.Run("WithProxyCheckAllowAll", func(b *testing.B) {
app := New(Config{
TrustProxy: true,
TrustProxyConfig: TrustProxyConfig{
Private: true,
Loopback: true,
LinkLocal: true,
},
})
c := app.AcquireCtx(&fasthttp.RequestCtx{})
c.Request().SetRequestURI("http://google.com/test")
c.Request().Header.Set(HeaderXForwardedHost, "google1.com")
b.ReportAllocs()
b.ResetTimer()
for n := 0; n < b.N; n++ {
c.IsProxyTrusted()
}
app.ReleaseCtx(c)
})
// Scenario with trusted proxy check allow private, loopback, and link-local in parallel
b.Run("WithProxyCheckAllowAllParallel", func(b *testing.B) {
app := New(Config{
TrustProxy: true,
TrustProxyConfig: TrustProxyConfig{
Private: true,
Loopback: true,
LinkLocal: true,
},
})
b.ReportAllocs()
b.ResetTimer()
b.RunParallel(func(pb *testing.PB) {
c := app.AcquireCtx(&fasthttp.RequestCtx{})
c.Request().SetRequestURI("http://google.com/")
c.Request().Header.Set(HeaderXForwardedHost, "google1.com")
for pb.Next() {
c.IsProxyTrusted()
}
app.ReleaseCtx(c)
})
})
// Scenario with trusted proxy check allow private, loopback, and link-local as subnets
b.Run("WithProxyCheckAllowAllowAllAsSubnets", func(b *testing.B) {
app := New(Config{
TrustProxy: true,
TrustProxyConfig: TrustProxyConfig{
Proxies: []string{
// Link-local
"169.254.0.0/16",
"fe80::/10",
// Loopback
"127.0.0.0/8",
"::1/128",
// Private
"10.0.0.0/8",
"172.16.0.0/12",
"192.168.0.0/16",
"fc00::/7",
},
},
})
c := app.AcquireCtx(&fasthttp.RequestCtx{})
c.Request().SetRequestURI("http://google.com/test")
c.Request().Header.Set(HeaderXForwardedHost, "google1.com")
b.ReportAllocs()
b.ResetTimer()
for n := 0; n < b.N; n++ {
c.IsProxyTrusted()
}
app.ReleaseCtx(c)
})
// Scenario with trusted proxy check allow private, loopback, and link-local as subnets in parallel
b.Run("WithProxyCheckAllowAllowAllAsSubnetsParallel", func(b *testing.B) {
app := New(Config{
TrustProxy: true,
TrustProxyConfig: TrustProxyConfig{
Proxies: []string{
// Link-local
"169.254.0.0/16",
"fe80::/10",
// Loopback
"127.0.0.0/8",
"::1/128",
// Private
"10.0.0.0/8",
"172.16.0.0/12",
"192.168.0.0/16",
"fc00::/7",
},
},
})
b.ReportAllocs()
b.ResetTimer()
@ -6243,8 +6493,10 @@ func Benchmark_Ctx_IsProxyTrusted(b *testing.B) {
// Scenario with trusted proxy check with subnet
b.Run("WithProxyCheckSubnet", func(b *testing.B) {
app := New(Config{
EnableTrustedProxyCheck: true,
TrustedProxies: []string{"0.0.0.0/8"},
TrustProxy: true,
TrustProxyConfig: TrustProxyConfig{
Proxies: []string{"0.0.0.0/8"},
},
})
c := app.AcquireCtx(&fasthttp.RequestCtx{})
c.Request().SetRequestURI("http://google.com/test")
@ -6260,8 +6512,10 @@ func Benchmark_Ctx_IsProxyTrusted(b *testing.B) {
// Scenario with trusted proxy check with subnet in parallel
b.Run("WithProxyCheckParallelSubnet", func(b *testing.B) {
app := New(Config{
EnableTrustedProxyCheck: true,
TrustedProxies: []string{"0.0.0.0/8"},
TrustProxy: true,
TrustProxyConfig: TrustProxyConfig{
Proxies: []string{"0.0.0.0/8"},
},
})
b.ReportAllocs()
b.ResetTimer()
@ -6279,8 +6533,10 @@ func Benchmark_Ctx_IsProxyTrusted(b *testing.B) {
// Scenario with trusted proxy check with multiple subnet
b.Run("WithProxyCheckMultipleSubnet", func(b *testing.B) {
app := New(Config{
EnableTrustedProxyCheck: true,
TrustedProxies: []string{"192.168.0.0/24", "10.0.0.0/16", "0.0.0.0/8"},
TrustProxy: true,
TrustProxyConfig: TrustProxyConfig{
Proxies: []string{"192.168.0.0/24", "10.0.0.0/16", "0.0.0.0/8"},
},
})
c := app.AcquireCtx(&fasthttp.RequestCtx{})
c.Request().SetRequestURI("http://google.com/test")
@ -6296,8 +6552,10 @@ func Benchmark_Ctx_IsProxyTrusted(b *testing.B) {
// Scenario with trusted proxy check with multiple subnet in parallel
b.Run("WithProxyCheckParallelMultipleSubnet", func(b *testing.B) {
app := New(Config{
EnableTrustedProxyCheck: true,
TrustedProxies: []string{"192.168.0.0/24", "10.0.0.0/16", "0.0.0.0/8"},
TrustProxy: true,
TrustProxyConfig: TrustProxyConfig{
Proxies: []string{"192.168.0.0/24", "10.0.0.0/16", "0.0.0.0/8"},
},
})
b.ReportAllocs()
b.ResetTimer()
@ -6315,17 +6573,19 @@ func Benchmark_Ctx_IsProxyTrusted(b *testing.B) {
// Scenario with trusted proxy check with all subnets
b.Run("WithProxyCheckAllSubnets", func(b *testing.B) {
app := New(Config{
EnableTrustedProxyCheck: true,
TrustedProxies: []string{
"127.0.0.0/8", // Loopback addresses
"169.254.0.0/16", // Link-Local addresses
"fe80::/10", // Link-Local addresses
"192.168.0.0/16", // Private Network addresses
"172.16.0.0/12", // Private Network addresses
"10.0.0.0/8", // Private Network addresses
"fc00::/7", // Unique Local addresses
"173.245.48.0/20", // My custom range
"0.0.0.0/8", // All IPv4 addresses
TrustProxy: true,
TrustProxyConfig: TrustProxyConfig{
Proxies: []string{
"127.0.0.0/8", // Loopback addresses
"169.254.0.0/16", // Link-Local addresses
"fe80::/10", // Link-Local addresses
"192.168.0.0/16", // Private Network addresses
"172.16.0.0/12", // Private Network addresses
"10.0.0.0/8", // Private Network addresses
"fc00::/7", // Unique Local addresses
"173.245.48.0/20", // My custom range
"0.0.0.0/8", // All IPv4 addresses
},
},
})
c := app.AcquireCtx(&fasthttp.RequestCtx{})
@ -6342,17 +6602,19 @@ func Benchmark_Ctx_IsProxyTrusted(b *testing.B) {
// Scenario with trusted proxy check with all subnets in parallel
b.Run("WithProxyCheckParallelAllSubnets", func(b *testing.B) {
app := New(Config{
EnableTrustedProxyCheck: true,
TrustedProxies: []string{
"127.0.0.0/8", // Loopback addresses
"169.254.0.0/16", // Link-Local addresses
"fe80::/10", // Link-Local addresses
"192.168.0.0/16", // Private Network addresses
"172.16.0.0/12", // Private Network addresses
"10.0.0.0/8", // Private Network addresses
"fc00::/7", // Unique Local addresses
"173.245.48.0/20", // My custom range
"0.0.0.0/8", // All IPv4 addresses
TrustProxy: true,
TrustProxyConfig: TrustProxyConfig{
Proxies: []string{
"127.0.0.0/8", // Loopback addresses
"169.254.0.0/16", // Link-Local addresses
"fe80::/10", // Link-Local addresses
"192.168.0.0/16", // Private Network addresses
"172.16.0.0/12", // Private Network addresses
"10.0.0.0/8", // Private Network addresses
"fc00::/7", // Unique Local addresses
"173.245.48.0/20", // My custom range
"0.0.0.0/8", // All IPv4 addresses
},
},
})
b.ReportAllocs()

View File

@ -831,7 +831,7 @@ app.Get("/", func(c fiber.Ctx) error {
## IsProxyTrusted
Checks trustworthiness of remote ip.
If [`EnableTrustedProxyCheck`](fiber.md#enabletrustedproxycheck) false, it returns true
If [`TrustProxy`](fiber.md#trustproxy) false, it returns true
IsProxyTrusted can check remote ip by proxy ranges and ip map.
```go title="Signature"
@ -841,10 +841,13 @@ func (c Ctx) IsProxyTrusted() bool
```go title="Example"
app := fiber.New(fiber.Config{
// EnableTrustedProxyCheck enables the trusted proxy check
EnableTrustedProxyCheck: true,
// TrustedProxies is a list of trusted proxy IP addresses
TrustedProxies: []string{"0.8.0.0", "0.8.0.1"},
// TrustProxy enables the trusted proxy check
TrustProxy: true,
// TrustProxyConfig allows for configuring trusted proxies.
// Proxies is a list of trusted proxy IP ranges/addresses
TrustProxyConfig: fiber.TrustProxyConfig{
Proxies: []string{"0.8.0.0", "0.8.0.1"},
}
})
@ -1640,7 +1643,7 @@ app.Post("/", func(c fiber.Ctx) error {
Contains the request protocol string: http or https for TLS requests.
:::info
Please use [`Config.EnableTrustedProxyCheck`](fiber.md#enabletrustedproxycheck) to prevent header spoofing, in case when your app is behind the proxy.
Please use [`Config.TrustProxy`](fiber.md#trustproxy) to prevent header spoofing, in case when your app is behind the proxy.
:::
```go title="Signature"

View File

@ -42,45 +42,45 @@ app := fiber.New(fiber.Config{
#### Config fields
| Property | Type | Description | Default |
|---------------------------------------------------------------------------------------|-------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------|
| <Reference id="appname">AppName</Reference> | `string` | This allows to setup app name for the app | `""` |
| <Reference id="bodylimit">BodyLimit</Reference> | `int` | Sets the maximum allowed size for a request body, if the size exceeds the configured limit, it sends `413 - Request Entity Too Large` response. | `4 * 1024 * 1024` |
| <Reference id="casesensitive">CaseSensitive</Reference> | `bool` | When enabled, `/Foo` and `/foo` are different routes. When disabled, `/Foo`and `/foo` are treated the same. | `false` |
| <Reference id="colorscheme">ColorScheme</Reference> | [`Colors`](https://github.com/gofiber/fiber/blob/master/color.go) | You can define custom color scheme. They'll be used for startup message, route list and some middlewares. | [`DefaultColors`](https://github.com/gofiber/fiber/blob/master/color.go) |
| <Reference id="compressedfilesuffixes">CompressedFileSuffixes</Reference> | `map[string]string` | Adds a suffix to the original file name and tries saving the resulting compressed file under the new file name. | `{"gzip": ".fiber.gz", "br": ".fiber.br", "zstd": ".fiber.zst"}` |
| <Reference id="concurrency">Concurrency</Reference> | `int` | Maximum number of concurrent connections. | `256 * 1024` |
| <Reference id="disabledefaultcontenttype">DisableDefaultContentType</Reference> | `bool` | When set to true, causes the default Content-Type header to be excluded from the Response. | `false` |
| <Reference id="disabledefaultdate">DisableDefaultDate</Reference> | `bool` | When set to true causes the default date header to be excluded from the response. | `false` |
| <Reference id="disableheadernormalizing">DisableHeaderNormalizing</Reference> | `bool` | By default all header names are normalized: conteNT-tYPE -&gt; Content-Type | `false` |
| <Reference id="disablekeepalive">DisableKeepalive</Reference> | `bool` | Disable keep-alive connections, the server will close incoming connections after sending the first response to the client | `false` |
| <Reference id="disablepreparsemultipartform">DisablePreParseMultipartForm</Reference> | `bool` | Will not pre parse Multipart Form data if set to true. This option is useful for servers that desire to treat multipart form data as a binary blob, or choose when to parse the data. | `false` |
| <Reference id="enableipvalidation">EnableIPValidation</Reference> | `bool` | If set to true, `c.IP()` and `c.IPs()` will validate IP addresses before returning them. Also, `c.IP()` will return only the first valid IP rather than just the raw header value that may be a comma separated string.<br /><br />**WARNING:** There is a small performance cost to doing this validation. Keep disabled if speed is your only concern and your application is behind a trusted proxy that already validates this header. | `false` |
| <Reference id="enablesplittingonparsers">EnableSplittingOnParsers</Reference> | `bool` | EnableSplittingOnParsers splits the query/body/header parameters by comma when it's true. <br /> <br /> For example, you can use it to parse multiple values from a query parameter like this: `/api?foo=bar,baz == foo[]=bar&foo[]=baz` | `false` |
| <Reference id="enabletrustedproxycheck">EnableTrustedProxyCheck</Reference> | `bool` | When set to true, fiber will check whether proxy is trusted, using TrustedProxies list. <br /><br />By default `c.Protocol()` will get value from X-Forwarded-Proto, X-Forwarded-Protocol, X-Forwarded-Ssl or X-Url-Scheme header, `c.IP()` will get value from `ProxyHeader` header, `c.Hostname()` will get value from X-Forwarded-Host header. <br /> If `EnableTrustedProxyCheck` is true, and `RemoteIP` is in the list of `TrustedProxies` `c.Protocol()`, `c.IP()`, and `c.Hostname()` will have the same behaviour when `EnableTrustedProxyCheck` disabled, if `RemoteIP` isn't in the list, `c.Protocol()` will return https in case when tls connection is handled by the app, or http otherwise, `c.IP()` will return RemoteIP() from fasthttp context, `c.Hostname()` will return `fasthttp.Request.URI().Host()` | `false` |
| <Reference id="errorhandler">ErrorHandler</Reference> | `ErrorHandler` | ErrorHandler is executed when an error is returned from fiber.Handler. Mounted fiber error handlers are retained by the top-level app and applied on prefix associated requests. | `DefaultErrorHandler` |
| <Reference id="getonly">GETOnly</Reference> | `bool` | Rejects all non-GET requests if set to true. This option is useful as anti-DoS protection for servers accepting only GET requests. The request size is limited by ReadBufferSize if GETOnly is set. | `false` |
| <Reference id="idletimeout">IdleTimeout</Reference> | `time.Duration` | The maximum amount of time to wait for the next request when keep-alive is enabled. If IdleTimeout is zero, the value of ReadTimeout is used. | `nil` |
| <Reference id="immutable">Immutable</Reference> | `bool` | When enabled, all values returned by context methods are immutable. By default, they are valid until you return from the handler; see issue [\#185](https://github.com/gofiber/fiber/issues/185). | `false` |
| <Reference id="jsondecoder">JSONDecoder</Reference> | `utils.JSONUnmarshal` | Allowing for flexibility in using another json library for decoding. | `json.Unmarshal` |
| <Reference id="jsonencoder">JSONEncoder</Reference> | `utils.JSONMarshal` | Allowing for flexibility in using another json library for encoding. | `json.Marshal` |
| <Reference id="passlocalstoviews">PassLocalsToViews</Reference> | `bool` | PassLocalsToViews Enables passing of the locals set on a fiber.Ctx to the template engine. See our **Template Middleware** for supported engines. | `false` |
| <Reference id="proxyheader">ProxyHeader</Reference> | `string` | This will enable `c.IP()` to return the value of the given header key. By default `c.IP()`will return the Remote IP from the TCP connection, this property can be useful if you are behind a load balancer e.g. _X-Forwarded-\*_. | `""` |
| <Reference id="readbuffersize">ReadBufferSize</Reference> | `int` | per-connection buffer size for requests' reading. This also limits the maximum header size. Increase this buffer if your clients send multi-KB RequestURIs and/or multi-KB headers \(for example, BIG cookies\). | `4096` |
| <Reference id="readtimeout">ReadTimeout</Reference> | `time.Duration` | The amount of time allowed to read the full request, including the body. The default timeout is unlimited. | `nil` |
| <Reference id="reducememoryusage">ReduceMemoryUsage</Reference> | `bool` | Aggressively reduces memory usage at the cost of higher CPU usage if set to true. | `false` |
| <Reference id="requestmethods">RequestMethods</Reference> | `[]string` | RequestMethods provides customizibility for HTTP methods. You can add/remove methods as you wish. | `DefaultMethods` |
| <Reference id="serverheader">ServerHeader</Reference> | `string` | Enables the `Server` HTTP header with the given value. | `""` |
| <Reference id="streamrequestbody">StreamRequestBody</Reference> | `bool` | StreamRequestBody enables request body streaming, and calls the handler sooner when given body is larger than the current limit. | `false` |
| <Reference id="strictrouting">StrictRouting</Reference> | `bool` | When enabled, the router treats `/foo` and `/foo/` as different. Otherwise, the router treats `/foo` and `/foo/` as the same. | `false` |
| <Reference id="structvalidator">StructValidator</Reference> | `StructValidator` | If you want to validate header/form/query... automatically when to bind, you can define struct validator. Fiber doesn't have default validator, so it'll skip validator step if you don't use any validator. | `nil` |
| <Reference id="trustedproxies">TrustedProxies</Reference> | `[]string` | Contains the list of trusted proxy IP's. Look at `EnableTrustedProxyCheck` doc. <br /> <br /> It can take IP or IP range addresses. | `nil` |
| <Reference id="unescapepath">UnescapePath</Reference> | `bool` | Converts all encoded characters in the route back before setting the path for the context, so that the routing can also work with URL encoded special characters | `false` |
| <Reference id="views">Views</Reference> | `Views` | Views is the interface that wraps the Render function. See our **Template Middleware** for supported engines. | `nil` |
| <Reference id="viewslayout">ViewsLayout</Reference> | `string` | Views Layout is the global layout for all template render until override on Render function. See our **Template Middleware** for supported engines. | `""` |
| <Reference id="writebuffersize">WriteBufferSize</Reference> | `int` | Per-connection buffer size for responses' writing. | `4096` |
| <Reference id="writetimeout">WriteTimeout</Reference> | `time.Duration` | The maximum duration before timing out writes of the response. The default timeout is unlimited. | `nil` |
| <Reference id="xmlencoder">XMLEncoder</Reference> | `utils.XMLMarshal` | Allowing for flexibility in using another XML library for encoding. | `xml.Marshal` |
| Property | Type | Description | Default |
|---------------------------------------------------------------------------------------|-------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------|
| <Reference id="appname">AppName</Reference> | `string` | This allows to setup app name for the app | `""` |
| <Reference id="bodylimit">BodyLimit</Reference> | `int` | Sets the maximum allowed size for a request body, if the size exceeds the configured limit, it sends `413 - Request Entity Too Large` response. | `4 * 1024 * 1024` |
| <Reference id="casesensitive">CaseSensitive</Reference> | `bool` | When enabled, `/Foo` and `/foo` are different routes. When disabled, `/Foo`and `/foo` are treated the same. | `false` |
| <Reference id="colorscheme">ColorScheme</Reference> | [`Colors`](https://github.com/gofiber/fiber/blob/master/color.go) | You can define custom color scheme. They'll be used for startup message, route list and some middlewares. | [`DefaultColors`](https://github.com/gofiber/fiber/blob/master/color.go) |
| <Reference id="compressedfilesuffixes">CompressedFileSuffixes</Reference> | `map[string]string` | Adds a suffix to the original file name and tries saving the resulting compressed file under the new file name. | `{"gzip": ".fiber.gz", "br": ".fiber.br", "zstd": ".fiber.zst"}` |
| <Reference id="concurrency">Concurrency</Reference> | `int` | Maximum number of concurrent connections. | `256 * 1024` |
| <Reference id="disabledefaultcontenttype">DisableDefaultContentType</Reference> | `bool` | When set to true, causes the default Content-Type header to be excluded from the Response. | `false` |
| <Reference id="disabledefaultdate">DisableDefaultDate</Reference> | `bool` | When set to true causes the default date header to be excluded from the response. | `false` |
| <Reference id="disableheadernormalizing">DisableHeaderNormalizing</Reference> | `bool` | By default all header names are normalized: conteNT-tYPE -&gt; Content-Type | `false` |
| <Reference id="disablekeepalive">DisableKeepalive</Reference> | `bool` | Disable keep-alive connections, the server will close incoming connections after sending the first response to the client | `false` |
| <Reference id="disablepreparsemultipartform">DisablePreParseMultipartForm</Reference> | `bool` | Will not pre parse Multipart Form data if set to true. This option is useful for servers that desire to treat multipart form data as a binary blob, or choose when to parse the data. | `false` |
| <Reference id="enableipvalidation">EnableIPValidation</Reference> | `bool` | If set to true, `c.IP()` and `c.IPs()` will validate IP addresses before returning them. Also, `c.IP()` will return only the first valid IP rather than just the raw header value that may be a comma separated string.<br /><br />**WARNING:** There is a small performance cost to doing this validation. Keep disabled if speed is your only concern and your application is behind a trusted proxy that already validates this header. | `false` |
| <Reference id="enablesplittingonparsers">EnableSplittingOnParsers</Reference> | `bool` | EnableSplittingOnParsers splits the query/body/header parameters by comma when it's true. <br /> <br /> For example, you can use it to parse multiple values from a query parameter like this: `/api?foo=bar,baz == foo[]=bar&foo[]=baz` | `false` |
| <Reference id="trustproxy">TrustProxy</Reference> | `bool` | When set to true, fiber will check whether proxy is trusted, using TrustProxyConfig.Proxies list. <br /><br />By default `c.Protocol()` will get value from X-Forwarded-Proto, X-Forwarded-Protocol, X-Forwarded-Ssl or X-Url-Scheme header, `c.IP()` will get value from `ProxyHeader` header, `c.Hostname()` will get value from X-Forwarded-Host header. <br /> If `TrustProxy` is true, and `RemoteIP` is in the list of `TrustProxyConfig.Proxies` `c.Protocol()`, `c.IP()`, and `c.Hostname()` will have the same behaviour when `TrustProxy` disabled, if `RemoteIP` isn't in the list, `c.Protocol()` will return https when a TLS connection is handled by the app, or http otherwise, `c.IP()` will return RemoteIP() from fasthttp context, `c.Hostname()` will return `fasthttp.Request.URI().Host()` | `false` |
| <Reference id="errorhandler">ErrorHandler</Reference> | `ErrorHandler` | ErrorHandler is executed when an error is returned from fiber.Handler. Mounted fiber error handlers are retained by the top-level app and applied on prefix associated requests. | `DefaultErrorHandler` |
| <Reference id="getonly">GETOnly</Reference> | `bool` | Rejects all non-GET requests if set to true. This option is useful as anti-DoS protection for servers accepting only GET requests. The request size is limited by ReadBufferSize if GETOnly is set. | `false` |
| <Reference id="idletimeout">IdleTimeout</Reference> | `time.Duration` | The maximum amount of time to wait for the next request when keep-alive is enabled. If IdleTimeout is zero, the value of ReadTimeout is used. | `nil` |
| <Reference id="immutable">Immutable</Reference> | `bool` | When enabled, all values returned by context methods are immutable. By default, they are valid until you return from the handler; see issue [\#185](https://github.com/gofiber/fiber/issues/185). | `false` |
| <Reference id="jsondecoder">JSONDecoder</Reference> | `utils.JSONUnmarshal` | Allowing for flexibility in using another json library for decoding. | `json.Unmarshal` |
| <Reference id="jsonencoder">JSONEncoder</Reference> | `utils.JSONMarshal` | Allowing for flexibility in using another json library for encoding. | `json.Marshal` |
| <Reference id="passlocalstoviews">PassLocalsToViews</Reference> | `bool` | PassLocalsToViews Enables passing of the locals set on a fiber.Ctx to the template engine. See our **Template Middleware** for supported engines. | `false` |
| <Reference id="proxyheader">ProxyHeader</Reference> | `string` | This will enable `c.IP()` to return the value of the given header key. By default `c.IP()`will return the Remote IP from the TCP connection, this property can be useful if you are behind a load balancer e.g. _X-Forwarded-\*_. | `""` |
| <Reference id="readbuffersize">ReadBufferSize</Reference> | `int` | per-connection buffer size for requests' reading. This also limits the maximum header size. Increase this buffer if your clients send multi-KB RequestURIs and/or multi-KB headers \(for example, BIG cookies\). | `4096` |
| <Reference id="readtimeout">ReadTimeout</Reference> | `time.Duration` | The amount of time allowed to read the full request, including the body. The default timeout is unlimited. | `nil` |
| <Reference id="reducememoryusage">ReduceMemoryUsage</Reference> | `bool` | Aggressively reduces memory usage at the cost of higher CPU usage if set to true. | `false` |
| <Reference id="requestmethods">RequestMethods</Reference> | `[]string` | RequestMethods provides customizability for HTTP methods. You can add/remove methods as you wish. | `DefaultMethods` |
| <Reference id="serverheader">ServerHeader</Reference> | `string` | Enables the `Server` HTTP header with the given value. | `""` |
| <Reference id="streamrequestbody">StreamRequestBody</Reference> | `bool` | StreamRequestBody enables request body streaming, and calls the handler sooner when given body is larger than the current limit. | `false` |
| <Reference id="strictrouting">StrictRouting</Reference> | `bool` | When enabled, the router treats `/foo` and `/foo/` as different. Otherwise, the router treats `/foo` and `/foo/` as the same. | `false` |
| <Reference id="structvalidator">StructValidator</Reference> | `StructValidator` | If you want to validate header/form/query... automatically when to bind, you can define struct validator. Fiber doesn't have default validator, so it'll skip validator step if you don't use any validator. | `nil` |
| <Reference id="trustproxyconfig">TrustProxyConfig</Reference> | `TrustProxyConfig` | Configure trusted proxy IP's. Look at `TrustProxy` doc. <br /> <br /> `TrustProxyConfig.Proxies` can take IP or IP range addresses. | `nil` |
| <Reference id="unescapepath">UnescapePath</Reference> | `bool` | Converts all encoded characters in the route back before setting the path for the context, so that the routing can also work with URL encoded special characters | `false` |
| <Reference id="views">Views</Reference> | `Views` | Views is the interface that wraps the Render function. See our **Template Middleware** for supported engines. | `nil` |
| <Reference id="viewslayout">ViewsLayout</Reference> | `string` | Views Layout is the global layout for all template render until override on Render function. See our **Template Middleware** for supported engines. | `""` |
| <Reference id="writebuffersize">WriteBufferSize</Reference> | `int` | Per-connection buffer size for responses' writing. | `4096` |
| <Reference id="writetimeout">WriteTimeout</Reference> | `time.Duration` | The maximum duration before timing out writes of the response. The default timeout is unlimited. | `nil` |
| <Reference id="xmlencoder">XMLEncoder</Reference> | `utils.XMLMarshal` | Allowing for flexibility in using another XML library for encoding. | `xml.Marshal` |
## Server listening

View File

@ -118,7 +118,7 @@ panic: [CORS] Configuration error: When 'AllowCredentials' is set to true, 'Allo
| AllowOrigins | `[]string` | AllowOrigins defines a list of origins that may access the resource. This supports subdomain matching, so you can use a value like "https://*.example.com" to allow any subdomain of example.com to submit requests. If the special wildcard `"*"` is present in the list, all origins will be allowed. | `["*"]` |
| AllowOriginsFunc | `func(origin string) bool` | `AllowOriginsFunc` is a function that dynamically determines whether to allow a request based on its origin. If this function returns `true`, the 'Access-Control-Allow-Origin' response header will be set to the request's 'origin' header. This function is only used if the request's origin doesn't match any origin in `AllowOrigins`. | `nil` |
| AllowPrivateNetwork | `bool` | Indicates whether the `Access-Control-Allow-Private-Network` response header should be set to `true`, allowing requests from private networks. This aligns with modern security practices for web applications interacting with private networks. | `false` |
| ExposeHeaders | `string` | ExposeHeaders defines whitelist headers that clients are allowed to access. | `[]` |
| ExposeHeaders | `string` | ExposeHeaders defines an allowlist of headers that clients are allowed to access. | `[]` |
| MaxAge | `int` | MaxAge indicates how long (in seconds) the results of a preflight request can be cached. If you pass MaxAge 0, the Access-Control-Max-Age header will not be added and the browser will use 5 seconds by default. To disable caching completely, pass MaxAge value negative. It will set the Access-Control-Max-Age header to 0. | `0` |
| Next | `func(fiber.Ctx) bool` | Next defines a function to skip this middleware when returned true. | `nil` |
@ -191,7 +191,7 @@ The `AllowHeaders` option specifies which headers are allowed in the actual requ
The `AllowCredentials` option indicates whether the response to the request can be exposed when the credentials flag is true. If `AllowCredentials` is set to `true`, the middleware adds the header `Access-Control-Allow-Credentials: true` to the response. To prevent security vulnerabilities, `AllowCredentials` cannot be set to `true` if `AllowOrigins` is set to a wildcard (`*`).
The `ExposeHeaders` option defines a whitelist of headers that clients are allowed to access. If `ExposeHeaders` is set to `"X-Custom-Header"`, the middleware adds the header `Access-Control-Expose-Headers: X-Custom-Header` to the response.
The `ExposeHeaders` option defines an allowlist of headers that clients are allowed to access. If `ExposeHeaders` is set to `"X-Custom-Header"`, the middleware adds the header `Access-Control-Expose-Headers: X-Custom-Header` to the response.
The `MaxAge` option indicates how long the results of a preflight request can be cached. If `MaxAge` is set to `3600`, the middleware adds the header `Access-Control-Max-Age: 3600` to the response.
@ -207,7 +207,7 @@ When configuring CORS, misconfiguration can potentially expose your application
- **Use Credentials Carefully**: If your application needs to support credentials in cross-origin requests, ensure `AllowCredentials` is set to `true` and specify exact origins in `AllowOrigins`. Do not use a wildcard origin in this case.
- **Limit Exposed Headers**: Only whitelist headers that are necessary for the client-side application by setting `ExposeHeaders` appropriately. This minimizes the risk of exposing sensitive information.
- **Limit Exposed Headers**: Only allowlist headers that are necessary for the client-side application by setting `ExposeHeaders` appropriately. This minimizes the risk of exposing sensitive information.
### Common Pitfalls

View File

@ -7,7 +7,7 @@ id: earlydata
The Early Data middleware for [Fiber](https://github.com/gofiber/fiber) adds support for TLS 1.3's early data ("0-RTT") feature.
Citing [RFC 8446](https://datatracker.ietf.org/doc/html/rfc8446#section-2-3), when a client and server share a PSK, TLS 1.3 allows clients to send data on the first flight ("early data") to speed up the request, effectively reducing the regular 1-RTT request to a 0-RTT request.
Make sure to enable fiber's `EnableTrustedProxyCheck` config option before using this middleware in order to not trust bogus HTTP request headers of the client.
Make sure to enable fiber's `TrustProxy` config option before using this middleware in order to not trust bogus HTTP request headers of the client.
Also be aware that enabling support for early data in your reverse proxy (e.g. nginx, as done with a simple `ssl_early_data on;`) makes requests replayable. Refer to the following documents before continuing:

View File

@ -55,6 +55,8 @@ We have made several changes to the Fiber app, including:
- EnablePrefork -> previously Prefork
- EnablePrintRoutes
- ListenerNetwork -> previously Network
- app.Config.EnabledTrustedProxyCheck -> has been moved to app.Config.TrustProxy
- TrustedProxies -> has been moved to TrustProxyConfig.Proxies
### new methods
@ -386,6 +388,35 @@ app.Get("*", static.New("./public/index.html"))
You have to put `*` to the end of the route if you don't define static route with `app.Use`.
:::
#### Trusted Proxies
We've renamed `EnableTrustedProxyCheck` to `TrustProxy` and moved `TrustedProxies` to `TrustProxyConfig`.
```go
// Before
app := fiber.New(fiber.Config{
// EnableTrustedProxyCheck enables the trusted proxy check.
EnableTrustedProxyCheck: true,
// TrustedProxies is a list of trusted proxy IP ranges/addresses.
TrustedProxies: []string{"0.8.0.0", "127.0.0.0/8", "::1/128"},
})
```
```go
// After
app := fiber.New(fiber.Config{
// TrustProxy enables the trusted proxy check
TrustProxy: true,
// TrustProxyConfig allows for configuring trusted proxies.
TrustProxyConfig: fiber.TrustProxyConfig{
// Proxies is a list of trusted proxy IP ranges/addresses.
Proxies: []string{"0.8.0.0"},
// Trust all loop-back IP addresses (127.0.0.0/8, ::1/128)
Loopback: true,
}
})
```
### 🗺 Router
The signatures for [`Add`](#middleware-registration) and [`Route`](#route-chaining) have been changed.

View File

@ -41,7 +41,7 @@ type Config struct {
// Optional. Default value []string{}
AllowHeaders []string
// ExposeHeaders defines a whitelist headers that clients are allowed to
// ExposeHeaders defines an allowlist of headers that clients are allowed to
// access.
//
// Optional. Default value []string{}.

View File

@ -173,17 +173,19 @@ func Test_EarlyData(t *testing.T) {
trustedRun(t, app)
})
t.Run("config with EnableTrustedProxyCheck", func(t *testing.T) {
t.Run("config with TrustProxy", func(t *testing.T) {
app := appWithConfig(t, &fiber.Config{
EnableTrustedProxyCheck: true,
TrustProxy: true,
})
untrustedRun(t, app)
})
t.Run("config with EnableTrustedProxyCheck and trusted TrustedProxies", func(t *testing.T) {
t.Run("config with TrustProxy and trusted TrustProxyConfig.Proxies", func(t *testing.T) {
app := appWithConfig(t, &fiber.Config{
EnableTrustedProxyCheck: true,
TrustedProxies: []string{
"0.0.0.0",
TrustProxy: true,
TrustProxyConfig: fiber.TrustProxyConfig{
Proxies: []string{
"0.0.0.0",
},
},
})
trustedRun(t, app)