Перейти к содержимому

Доверенные прокси

Gin позволяет указать, какие заголовки содержат реальный IP клиента (если есть), а также указать, каким прокси (или прямым клиентам) вы доверяете устанавливать эти заголовки.

Почему важна конфигурация доверенных прокси

Заголовок раздела «Почему важна конфигурация доверенных прокси»

Когда ваше приложение находится за обратным прокси (Nginx, HAProxy, облачным балансировщиком нагрузки и т.д.), прокси передаёт оригинальный IP-адрес клиента в заголовках, таких как X-Forwarded-For или X-Real-Ip. Проблема в том, что любой клиент может установить эти заголовки. Без правильной конфигурации доверенных прокси злоумышленник может подделать X-Forwarded-For чтобы:

  • Обойти контроль доступа на основе IP — Если ваше приложение ограничивает определённые маршруты внутренним диапазоном IP (например, 10.0.0.0/8), злоумышленник может отправить X-Forwarded-For: 10.0.0.1 с публичного IP и полностью обойти ограничение.
  • Отравить логи и аудиторские записи — Поддельные IP делают расследование инцидентов ненадёжным, поскольку вы больше не можете отследить запросы до реального источника.
  • Обойти ограничение частоты запросов — Если ограничение частоты привязано к ClientIP(), каждый запрос может заявить другой IP-адрес, чтобы избежать ограничения.

SetTrustedProxies решает эту проблему, сообщая Gin, какие сетевые адреса являются легитимными прокси. Когда ClientIP() разбирает цепочку X-Forwarded-For, он доверяет только записям, добавленным этими прокси, и отбрасывает всё, что мог добавить клиент. Если запрос приходит напрямую (не от доверенного прокси), заголовки перенаправления полностью игнорируются, и используется необработанный удалённый адрес.

Используйте функцию SetTrustedProxies() на вашем gin.Engine для указания сетевых адресов или CIDR сетей, от которых можно доверять заголовкам запросов клиентов, связанным с IP. Это могут быть IPv4-адреса, IPv4 CIDR, IPv6-адреса или IPv6 CIDR.

Внимание: Gin по умолчанию доверяет всем прокси, если вы не укажете доверенный прокси с помощью указанной функции, это НЕ безопасно. В то же время, если вы не используете прокси, вы можете отключить эту функцию с помощью Engine.SetTrustedProxies(nil), тогда Context.ClientIP() будет возвращать удалённый адрес напрямую, избегая ненужных вычислений.

import (
"fmt"
"github.com/gin-gonic/gin"
)
func main() {
router := gin.Default()
router.SetTrustedProxies([]string{"192.168.1.2"})
router.GET("/", func(c *gin.Context) {
// If the client is 192.168.1.2, use the X-Forwarded-For
// header to deduce the original client IP from the trust-
// worthy parts of that header.
// Otherwise, simply return the direct client IP
fmt.Printf("ClientIP: %s\n", c.ClientIP())
})
router.Run()
}

Примечание: Если вы используете CDN-сервис, вы можете установить Engine.TrustedPlatform для пропуска проверки TrustedProxies — он имеет более высокий приоритет, чем TrustedProxies. Смотрите пример ниже:

import (
"fmt"
"github.com/gin-gonic/gin"
)
func main() {
router := gin.Default()
// Use predefined header gin.PlatformXXX
// Google App Engine
router.TrustedPlatform = gin.PlatformGoogleAppEngine
// Cloudflare
router.TrustedPlatform = gin.PlatformCloudflare
// Fly.io
router.TrustedPlatform = gin.PlatformFlyIO
// Or, you can set your own trusted request header. But be sure your CDN
// prevents users from passing this header! For example, if your CDN puts
// the client IP in X-CDN-Client-IP:
router.TrustedPlatform = "X-CDN-Client-IP"
router.GET("/", func(c *gin.Context) {
// If you set TrustedPlatform, ClientIP() will resolve the
// corresponding header and return IP directly
fmt.Printf("ClientIP: %s\n", c.ClientIP())
})
router.Run()
}