Middleware
Middlewares globais (aplicados a todas as rotas)
Registrados no bootstrap — já ativos por padrão:
| Middleware | Descrição |
|---|---|
Recovery() |
Captura panics — mostra debug page (dev) ou 500 (prod) |
MaxBodySize(32MB) |
Limita o tamanho do body — defesa contra payloads gigantes |
SecureHeaders |
HSTS, X-Frame-Options, CSP (configurável — ver abaixo) — apenas em production (APP_ENV=development não ativa) |
AllowedHosts() |
Bloqueia hosts não autorizados (ignorado em dev) |
csrf.Middleware |
Valida token CSRF em métodos não seguros |
Middlewares opcionais
| Middleware | Uso | Descrição |
|---|---|---|
Compress |
r.Use(compress.Compress) |
Compressão gzip das respostas |
CORS(origins) |
r.Use(secmiddleware.CORS(...)) |
Cabeçalhos CORS para as origens permitidas |
RequireAuth(a) |
por rota ou global | Exige Bearer token JWT — APIs stateless |
RequireLogin(store, url) |
por rota ou global | Exige sessão ativa — views SSR; redireciona para url se não autenticado |
RateLimit(max, janela) |
por rota (recomendado no login) | Máximo de max requisições por IP por janela; excedentes recebem 429 |
LocalhostOnly |
por rota (ex: dashboards internos) | 403 para qualquer IP que não seja loopback — é o que protege /kyrux/debug/ |
MaxBodySize(n) |
por rota, além do global de 32MB | Limite específico menor (ou maior) para uma rota (ex: upload) |
import (
"kyrux/core/compress"
secmiddleware "kyrux/core/security/middleware"
)
// Compressão gzip
r.Use(compress.Compress)
// CORS
r.Use(secmiddleware.CORS([]string{"https://meusite.com.br"}))
// Exigir sessão ativa em views SSR — redireciona para /login/ se não autenticado
r.Use(secmiddleware.RequireLogin(fw.Sessions, "/login/"))
// Exigir JWT em rotas de API
r.Use(secmiddleware.RequireAuth(fw.Auth))
// Rate limit por rota — essencial no login (Argon2 é caro por design):
router.Path("POST", "/login/",
secmiddleware.RateLimit(10, time.Minute)(views.LoginPost(fw)), "login_post"),
Middleware personalizado
func LogMiddleware(next router.HandlerFunc) router.HandlerFunc {
return func(ctx *router.Context) {
log.Printf("%s %s", ctx.Request.Method, ctx.Request.URL.Path)
next(ctx)
}
}
// Aplicar globalmente
r.Use(LogMiddleware)
Middleware por rota (via wrapper)
func Register(r *router.Router) {
// Rota SSR protegida por sessão
loginRequired := secmiddleware.RequireLogin(fw.Sessions, "/login/")
r.Handle("GET /dashboard/", loginRequired(func(ctx *router.Context) {
render.For("painel").Render(ctx, "dashboard.html", nil)
}))
// Rota de API protegida por JWT
jwtRequired := secmiddleware.RequireAuth(fw.Auth)
r.Handle("GET /api/perfil/", jwtRequired(func(ctx *router.Context) {
claims := ctx.Get("claims").(*auth.Claims)
ctx.JSON(200, map[string]string{"user_id": claims.UserID})
}))
}
Content-Security-Policy (CSP) configurável
A CSP enviada por SecureHeaders em produção tem um padrão estrito
(secmiddleware.DefaultCSP: default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:) — configurável em
dois níveis, sem precisar editar core/:
Global — troca a política do site inteiro, normalmente a partir de
CSP_POLICY no .env (o bootstrap já chama isso sozinho):
secmiddleware.SetCSP(cfg.Security.CSPPolicy) // policy vazia mantém o DefaultCSP
Por rota — sobrescreve só numa página que precisa de uma exceção pontual (um script/iframe de terceiro só ali — ex: um mapa incorporado ou um provedor de captcha), sem afrouxar a política padrão do resto do site:
mapaCSP := "default-src 'self'; frame-src https://www.google.com/maps/"
router.Path("GET", "/contato/",
secmiddleware.CSPOverride(mapaCSP)(views.ContatoView(fw)), "contato"),
Funciona porque middleware por rota roda depois do global (SecureHeaders
inclusive) na cadeia — o Set do CSPOverride substitui, nunca soma, o
header que SecureHeaders já escreveu. Também funciona sem
SecureHeaders ativo (development): só adiciona um header a mais,
inofensivo.