Middleware e proteção
Middlewares Web/API presentes, ordem de execução e perfis de rate limit.
Contrato
O Router recebe middlewares por rota/grupo e chama handle(Request $request, ...$args). Cadastre nomes das classes corretamente e inclua o arquivo/classe no autoload da aplicação. A cadeia executa em ordem antes do controller; middlewares podem responder e encerrar a requisição.
Implementações existentes
AuthMiddlewareexige sessão autenticada.GuestMiddlewarerestringe rotas a visitantes.RoleMiddleware:$roleaplica verificação de role.CsrfMiddlewareprotege mutações Web.RateLimitMiddleware:$profileusa arquivos JSON com lock.SecurityHeadersMiddlewareadiciona cabeçalhos de segurança.DevelopmentMiddlewareexiste para acesso de desenvolvimento.ApiAuthMiddlewareexige token Bearer válido.CorsMiddlewareaplica política CORS da API e responde preflight.
Perfis de rate limit
login: 5 tentativas/15 min por IP+email.register: 5/60 min por IP.forgot: 3/60 min por IP.api: 60/min por IP.default: 30/min por IP.
Login/registro/esqueci senha falham fechado em falha de escrita; API/default preservam disponibilidade. Resposta limitada é HTTP 429 com Retry-After.
CORS
CORS_ALLOWED_ORIGINS recebe origens separadas por vírgula. Wildcard só é aceito fora de produção; métodos/headers permitidos estão em config/api.php. OPTIONS termina com 204 no middleware.
$router->group([
'prefix' => '/api/v1',
'middleware' => ['CorsMiddleware', 'RateLimitMiddleware:api'],
], function (Router $r): void {
$r->get('/users', [UserApiController::class, 'index'], ['ApiAuthMiddleware']);
});Pipeline e ordem de aplicação
Router reúne primeiro os middlewares herdados dos grupos e depois os específicos da rota; executa a lista antes da action. Use middleware para decisões transversais (autenticação, CSRF, CORS, limite de taxa), não para renderizar a View de um recurso.
$router->group([
'prefix' => '/api/v1',
'middleware' => ['CorsMiddleware', 'RateLimitMiddleware:api'],
], function (Router $r): void {
$r->get('/users', [UserApiController::class, 'index'], ['ApiAuthMiddleware']);
});Esta estrutura corresponde ao grupo real de routes/api.php: CORS e rate limit cercam todos os endpoints; ApiAuthMiddleware é específico das rotas privadas. Na Web, /dashboard aplica AuthMiddleware ao grupo e CSRF fica nas actions POST.
Perfis de rate limit e decisão operacional
| Perfil | Limite/janela | Chave |
|---|---|---|
| login | 5 por 15 min | IP + e-mail normalizado |
| register | 5 por 60 min | IP |
| forgot | 3 por 60 min | IP |
| api | 60 por 1 min | IP |
| default | 30 por 1 min | IP |
O limitador grava arquivos em storage/cache/ratelimit. Se não conseguir registrar tentativas, login/register/forgot falham fechado; api/default preservam disponibilidade. Uma resposta bloqueada usa HTTP 429 e Retry-After. Esse armazenamento local não é um contador distribuído para vários nós.
CorsMiddleware lê CORS_ALLOWED_ORIGINS; origens autorizadas devem ser explícitas em produção. OPTIONS pode terminar no middleware com 204. TRUSTED_PROXIES também afeta identificação do IP: não confie em X-Forwarded-* vindo diretamente do cliente. Veja autenticação e CSRF.
Rate limit: chave, armazenamento e respostas
Os perfis são configurados em RateLimitMiddleware::$profiles; cada requisição permitida também incrementa o contador sob lock exclusivo flock(). O arquivo fica em storage/cache/ratelimit/. É um armazenamento local por instância de aplicação, não um contador distribuído entre vários nós.
| Perfil | Limite/janela | Chave | Falha ao gravar |
|---|---|---|---|
login | 5 / 900 s | IP + e-mail lido de $_POST['email'] | Fail closed: responde 429 com espera de 60 s. |
register | 5 / 3600 s | IP | Fail closed. |
forgot | 3 / 3600 s | IP | Fail closed. |
api | 60 / 60 s | IP | Fail open: registra falha e permite a requisição. |
default | 30 / 60 s | IP | Fail open: registra falha e permite a requisição. |
O bloqueio acontece quando attempts > max, então o valor máximo ainda é admitido; a resposta usa HTTP 429 e header Retry-After. Requisições AJAX/JSON recebem JSON com success=false, message e retry_after; navegador recebe flash e redirect seguro.
$_POST, não do corpo JSON decodificado pelo FormRequest. Em login JSON, esse componente pode ficar vazio; nessa entrada o perfil se comporta na prática como limite por IP. Não documente o perfil como limite por IP+e-mail para payload JSON.Quando o limitador identifica IP por cabeçalhos encaminhados, configure TRUSTED_PROXIES só com proxies controlados; não confie em X-Forwarded-For enviado diretamente pelo cliente. Consulte Configuração e Autenticação.
Contratos dos middlewares de autenticação e ambiente
Os middlewares recebem handle(Request $request): void; podem encerrar a resposta, redirecionar ou deixar a execução prosseguir. AuthMiddleware guarda autenticação e direciona o navegador para auth/login quando não autorizado. RoleMiddleware espera que a autenticação já tenha sido checada antes (inclua AuthMiddleware primeiro); a role padrão do middleware é admin. Falhas de autorização podem responder 403 em navegador e JSON em requisição API conforme o header de aceitação; não confunda isso com permissões por objeto/ACL.
DevelopmentMiddleware::handle(Request $request): void só permite a rota quando APP_ENV é literalmente local; em qualquer outro ambiente responde 404. Isso é uma trava por ambiente, não autorização por usuário ou IP; não a use como proteção de dado nem suponha disponibilidade em staging.
AuthMiddleware e RoleMiddleware chamam redirect('auth/login')/redirect('dashboard') sem barra inicial. O helper pode substituir esses destinos por APP_URL ou /; não prometa que o browser chegará às rotas nomeadas. O caminho de falha de role de navegador define 403 antes do redirect; para API/AJAX o middleware emite JSON 403 e encerra.SecurityHeadersMiddleware é global
Além de middleware por rota, SecurityHeadersMiddleware é registrado globalmente no bootstrap. O código define CSP com nonce e diretivas; Permissions-Policy; remove X-Powered-By; e adiciona HSTS somente em produção via HTTPS. Em scripts inline, use cspNonce() para fornecer o nonce compatível; não enfraqueça a CSP para contornar erro de template.