diff --git a/app.go b/app.go
index e0240d3c16..5c9cd73d03 100644
--- a/app.go
+++ b/app.go
@@ -344,6 +344,9 @@ type Config struct { //nolint:govet // Aligning the struct fields is not necessa
// 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.
//
+ // If you wish to trust all internal ip addresses (loopback, private, and link-local unicast)
+ // without manually adding them to TrustedProxies whitelist, enable TrustInternalIPs.
+ //
// Default: false
EnableTrustedProxyCheck bool `json:"enable_trusted_proxy_check"`
@@ -354,6 +357,11 @@ type Config struct { //nolint:govet // Aligning the struct fields is not necessa
trustedProxiesMap map[string]struct{}
trustedProxyRanges []*net.IPNet
+ // Read EnableTrustedProxyCheck doc.
+ //
+ // Default: false
+ TrustInternalIPs bool `json:"trust_internal_ips"`
+
// 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
// WARNING: this has a performance cost associated with it.
diff --git a/ctx.go b/ctx.go
index 4d7417ee2a..d7a65bbb53 100644
--- a/ctx.go
+++ b/ctx.go
@@ -1828,6 +1828,10 @@ func (c *DefaultCtx) IsProxyTrusted() bool {
ip := c.fasthttp.RemoteIP()
+ if c.app.config.TrustInternalIPs && c.isInternalHost(ip) {
+ return true
+ }
+
if _, trusted := c.app.config.trustedProxiesMap[ip.String()]; trusted {
return true
}
@@ -1841,9 +1845,14 @@ func (c *DefaultCtx) IsProxyTrusted() bool {
return false
}
+// isInternalHost will return true if address is an internal address.
+func (*DefaultCtx) isInternalHost(ip net.IP) bool {
+ return ip.IsLoopback() || ip.IsPrivate() || ip.IsLinkLocalUnicast()
+}
+
var localHosts = [...]string{"127.0.0.1", "::1"}
-// IsLocalHost will return true if address is a localhost address.
+// isLocalHost will return true if address is a localhost address.
func (*DefaultCtx) isLocalHost(address string) bool {
for _, h := range localHosts {
if address == h {
diff --git a/ctx_test.go b/ctx_test.go
index 6dcd74109e..d207710c04 100644
--- a/ctx_test.go
+++ b/ctx_test.go
@@ -1714,6 +1714,16 @@ func Test_Ctx_IsProxyTrusted(t *testing.T) {
c := app.AcquireCtx(&fasthttp.RequestCtx{})
require.False(t, c.IsProxyTrusted())
}
+
+ {
+ app := New(Config{
+ EnableTrustedProxyCheck: true,
+
+ TrustInternalIPs: true,
+ })
+ c := app.AcquireCtx(&fasthttp.RequestCtx{})
+ require.False(t, c.IsProxyTrusted())
+ }
}
// go test -run Test_Ctx_Hostname
@@ -6351,4 +6361,40 @@ func Benchmark_Ctx_IsProxyTrusted(b *testing.B) {
app.ReleaseCtx(c)
})
})
+
+ // Scenario with trusted proxy check with TrustInternalIPs true
+ b.Run("WithProxyCheckOnlyTrustInternalIPs", func(b *testing.B) {
+ app := New(Config{
+ EnableTrustedProxyCheck: true,
+ TrustInternalIPs: 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 with all subnets with TrustInternalIPs true in parallel
+ b.Run("WithProxyCheckParallelOnlyTrustInternalIPs", func(b *testing.B) {
+ app := New(Config{
+ EnableTrustedProxyCheck: true,
+ TrustInternalIPs: 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)
+ })
+ })
}
diff --git a/docs/api/ctx.md b/docs/api/ctx.md
index 2640512a3e..60e98dcfee 100644
--- a/docs/api/ctx.md
+++ b/docs/api/ctx.md
@@ -812,7 +812,7 @@ app.Get("/", func(c fiber.Ctx) error {
## IsFromLocal
-Returns true if request came from localhost
+Returns true if request came from localhost.
```go title="Signature"
func (c Ctx) IsFromLocal() bool {
@@ -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 [`EnableTrustedProxyCheck`](fiber.md#enabletrustedproxycheck) false, it returns true.
IsProxyTrusted can check remote ip by proxy ranges and ip map.
```go title="Signature"
@@ -845,6 +845,8 @@ app := fiber.New(fiber.Config{
EnableTrustedProxyCheck: true,
// TrustedProxies is a list of trusted proxy IP addresses
TrustedProxies: []string{"0.8.0.0", "0.8.0.1"},
+ // TrustInternalIPs can be used to trust all private/loopback/linklocal-unicast addresses
+ TrustInternalIPs: true,
})
diff --git a/docs/api/fiber.md b/docs/api/fiber.md
index 16f23b9e61..bd3190deaf 100644
--- a/docs/api/fiber.md
+++ b/docs/api/fiber.md
@@ -48,7 +48,7 @@ app := fiber.New(fiber.Config{
| BodyLimit | `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` |
| CaseSensitive | `bool` | When enabled, `/Foo` and `/foo` are different routes. When disabled, `/Foo`and `/foo` are treated the same. | `false` |
| ColorScheme | [`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) |
-| CompressedFileSuffixes | `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"}` |
+| CompressedFileSuffixes | `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"}` |
| Concurrency | `int` | Maximum number of concurrent connections. | `256 * 1024` |
| DisableDefaultContentType | `bool` | When set to true, causes the default Content-Type header to be excluded from the Response. | `false` |
| DisableDefaultDate | `bool` | When set to true causes the default date header to be excluded from the response. | `false` |
@@ -75,6 +75,7 @@ app := fiber.New(fiber.Config{
| StrictRouting | `bool` | When enabled, the router treats `/foo` and `/foo/` as different. Otherwise, the router treats `/foo` and `/foo/` as the same. | `false` |
| StructValidator | `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` |
| TrustedProxies | `[]string` | Contains the list of trusted proxy IP's. Look at `EnableTrustedProxyCheck` doc.
It can take IP or IP range addresses. | `nil` |
+| TrustInternalIPs | `bool` | If `EnableTrustedProxyCheck` is `true`, automatically trust all private, loopback, and link-local unicast IP addresses. | `false` |
| UnescapePath | `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` |
| Views | `Views` | Views is the interface that wraps the Render function. See our **Template Middleware** for supported engines. | `nil` |
| ViewsLayout | `string` | Views Layout is the global layout for all template render until override on Render function. See our **Template Middleware** for supported engines. | `""` |