A segurança começa antes dos controllers
HttpServer aplica middleware de segurança antes que qualquer handler de controller seja invocado. Esse wiring na inicialização dá a todo endpoint a mesma política de base desde a primeira requisição, sem exigir decoração explícita em cada rota. A pilha de segurança roda em uma ordem fixa que o código do usuário não pode reordenar, garantindo que hardening de headers e limites de body sejam sempre a primeira linha de defesa.
Quando HTTP está habilitado, o runtime conecta essas proteções nesta sequência exata:
- Remoção opcional dos headers
X-Powered-ByeServer(hidePoweredBy, com padrãotrue). - SecurityHeadersMiddleware envolvendo secureHeaders do Hono, a menos que
headersesteja comofalse. - CorsMiddleware envolvendo cors do Hono, apenas quando
security.corsestiver configurado. - BodyLimitMiddleware envolvendo bodyLimit do Hono, a menos que
bodyLimitesteja comofalse. - Instâncias globais de middleware vindas de @HttpClientModule().
- Guards globais de @HttpClientModule() por meio de GuardExecutor.
- Guards por controller e por rota de @HttpGuards por meio de GuardExecutor.
Depois dessa pilha, roda o middleware em nível de rota vindo de decorators @HttpUseMiddleware, e por fim o próprio handler é executado. Esse modelo em camadas significa que decisões de guard acontecem depois do hardening de base, e decisões de middleware acontecem depois que os headers de segurança já foram definidos.
Valores padrão de segurança
Todo header e limite de segurança tem um padrão seguro vindo de SECURITY_DEFAULTS. Você só precisa sobrescrever valores quando sua implantação exigir comportamento diferente. Quando um subcampo é omitido, o padrão se aplica. Quando é definido como false, aquela camada é desativada por completo.
Configuração de segurança e middleware
import { defineHttp } from '@spraxium/http';
import { AppModule } from '../src/app.module';
export const httpPlugin = defineHttp({
enabled: true,
host: process.env.HTTP_HOST ?? '0.0.0.0',
port: Number(process.env.HTTP_PORT ?? 3000),
apiKey: process.env.HTTP_API_KEY ?? '',
sharding: false,
security: {
hidePoweredBy: true,
headers: {
frameOptions: 'DENY',
contentSecurityPolicy: "default-src 'none'",
referrerPolicy: 'no-referrer',
hstsMaxAge: 31_536_000,
hstsIncludeSubDomains: true,
},
cors: {
origins: ['https://admin.example.com'],
methods: ['GET', 'POST', 'PATCH'],
allowedHeaders: ['Content-Type', 'Authorization', 'X-Api-Key'],
credentials: true,
maxAge: 600,
},
bodyLimit: { maxSize: 256_000 },
},
module: AppModule,
});Pipeline de guards e a interface HttpGuard
Guards são a fronteira de autenticação e autorização. Todo guard implementa a interface HttpGuard, que define um único método: canActivate(ctx: Context): Promise<boolean>. Guards declarados em @HttpClientModule() são instanciados pelo sistema de injeção de dependência na inicialização e executados em sequência por GuardExecutor em toda requisição depois da pilha de middleware de segurança. Nenhuma chamada manual a new é necessária ao declarar guards no módulo.
Quando GuardExecutor encontra um guard que retorna false, ele interrompe a execução imediatamente e define a resposta como { ok: false, error: 'Unauthorized' } com status 401. Nenhum outro guard, middleware ou handler é executado. Esse comportamento de short-circuit significa que a ordem dos guards importa: coloque primeiro o caso de rejeição mais comum para evitar checagens caras em requisições não autenticadas.
A resposta 401 da rejeição de guard é injetada diretamente, e não pelo handler onError. Isso significa que falhas de guard são finais e não podem ser capturadas nem modificadas por middleware downstream. O formato da resposta segue o mesmo envelope usado pelos outros erros, então clientes não precisam de tratamento especial para rejeições de guard.
@HttpGuards para proteção por controller e por rota
@HttpGuards é um decorator de classe ou método que restringe o escopo do guard a um controller específico ou a uma rota individual. Ao contrário de guards globais, que protegem todo endpoint, @HttpGuards mira um ponto único da superfície de roteamento. O decorator aceita uma ou mais classes de guard cujas instâncias são resolvidas pelo mesmo sistema de DI usado por services e controllers.
@HttpGuards em nível de classe roda em toda rota do controller. @HttpGuards em nível de método roda apenas naquele handler específico. Quando os dois níveis estão presentes, guards de classe executam primeiro, seguidos pelos guards de método. Essa organização em camadas permite proteger um controller inteiro com uma política compartilhada e adicionar validação extra por rota sem repetir o guard comum.
import {
HttpController,
HttpGet,
HttpPost,
HttpGuards,
HttpParam,
HttpBody,
} from '@spraxium/http';
import { AdminGuard } from '../guards/admin.guard';
import { SuperAdminGuard } from '../guards/super-admin.guard';
@HttpGuards(AdminGuard)
@HttpController('/admin')
export class AdminHttpController {
@HttpGet('/stats')
getStats() {
return { ok: true, activeUsers: 42 };
}
@HttpGuards(SuperAdminGuard)
@HttpPost('/reset')
reset(@HttpParam('confirm') \_confirm: string) {
return { ok: true };
}
}ApiKeyGuard em detalhe
ApiKeyGuard é o guard nativo que acompanha o pacote. Ele lê o header x-api-key da requisição e o compara com o valor fornecido na construção. A comparação usa timingSafeEqual do módulo crypto do Node para evitar ataques de timing que poderiam vazar informação sobre o tamanho da chave ou seu prefixo.
O guard rejeita requisições em três cenários: header ausente, retornando false imediatamente; buffers com tamanho diferente, retornando false sem comparar, para respeitar a exigência de tamanhos iguais do timingSafeEqual; e valores diferentes, retornando false depois da comparação em tempo constante. Essa estratégia de rejeição em camadas evita expor o tamanho da chave por diferenças no tempo de resposta.
ApiKeyGuard é ativado automaticamente quando apiKey é definido em defineHttp. O runtime cria e registra internamente a instância do guard, então você não precisa declará-la em @HttpClientModule(). O array guards fica reservado para guards customizados que exigem dependências resolvidas pela sua DI.
Modelo de execução de middleware
O pacote oferece dois pontos de entrada para middleware, com escopos e mecanismos de registro diferentes.
Instâncias globais de middleware são declaradas em @HttpClientModule() como um array de objetos HttpMiddleware. Elas são registradas no caminho '*' do Hono, o que significa que rodam em toda requisição depois da pilha de segurança, mas antes da execução dos guards. Use middleware global para preocupações transversais que se aplicam sem condição, como logging de requisição via LoggerMiddleware.
Middleware em nível de rota usa tokens de classe e o decorator @HttpUseMiddleware. O token de classe precisa ser mapeado para uma instância concreta em @HttpClientModule(). RouteBuilder resolve o token durante o binding da rota e insere o handler de middleware antes do handler da rota na cadeia do Hono. @HttpUseMiddleware em nível de classe roda antes de @HttpUseMiddleware em nível de método, e ambos rodam depois dos guards e do middleware global.
Todo middleware implementa a interface HttpMiddleware: handle(ctx: Context, next: Next): Promise<undefined | Response>. Chamar await next() passa o controle ao próximo middleware ou handler. Não chamar next() encerra a requisição naquele middleware, que é justamente como RateLimitMiddleware rejeita requisições acima do limite.
Referência de middleware embutido
Estratégia de rate limiting
RateLimitMiddleware implementa um rate limiter em memória com janela deslizante. Cada cliente é identificado pelo valor do header x-forwarded-for, ou por 'unknown' quando o header está ausente. O middleware mantém um Map<string, WindowEntry> em que cada entrada rastreia a contagem de requisições e o timestamp de início da janela.
Quando uma requisição chega, o middleware verifica se a janela atual expirou. Se expirou, a entrada é reiniciada. Se a janela ainda estiver ativa e a contagem ultrapassar max, o middleware responde com { ok: false, error: 'Too many requests' } no status 429 sem chamar next(). Um intervalo periódico de limpeza remove entradas de clientes que não enviaram requisições dentro da duração da janela.
Para usar rate limiting por rota, estenda RateLimitMiddleware com sua própria configuração e registre a instância em middlewareProviders. Esse padrão mantém a configuração de rate limit visível no nível da classe de middleware e evita números mágicos em argumentos de decorator.
Confiança em proxy e identificação de IP
O middleware de rate limit lê x-forwarded-for diretamente, sem verificar a origem. Em produção atrás de proxies reversos ou load balancers, isso significa que você precisa garantir que sua infraestrutura de proxy defina esse header corretamente. Se o header estiver ausente, todas as requisições ficam agrupadas sob a chave 'unknown', o que na prática cria um único bucket global de rate limit. Se o header puder ser falsificado porque o proxy não o sobrescreve, atacantes podem contornar o rate limit enviando IPs falsos no header.
A interface SecurityConfig inclui um campo trustedProxies com um array proxies, cujo padrão é ['127.0.0.1', '::1']. Use esse campo para documentar sua configuração de proxies confiáveis e garanta que sua arquitetura de implantação só permita que proxies confiáveis definam headers de encaminhamento.
Checklist operacional de hardening
Comece com padrões seguros
Habilite headers de base e limites de body, a menos que exista um requisito claro para relaxá-los. Uma postura de negação por padrão é mais fácil de auditar do que uma postura permissiva remendada depois. Sobrescreva headers individuais apenas quando sua implantação exigir.
Registre guards globais no AppModule
Declare classes de guard customizadas em @HttpClientModule()
para proteger todo endpoint com a mesma política. O framework resolve as
dependências dos guards a partir do container de DI, então guards podem
injetar services como AppEnv sem wiring manual. Use apiKey em
defineHttp para ativar ApiKeyGuard automaticamente, sem declará-lo no
array de guards.
Restrinja a proteção com @HttpGuards
Aplique @HttpGuards em nível de controller ou método para endpoints que exigem lógica de autorização diferente ou adicional. Isso mantém o guard global focado na política de base e leva checagens específicas de rota para o ponto de declaração. Guards de classe executam antes de guards de método no mesmo controller.
Adicione LoggerMiddleware para observabilidade
Registre new LoggerMiddleware() em @HttpClientModule() para
obter logging básico de requisições. O middleware registra método, caminho,
status e duração. Status codes 400 ou superiores são registrados em nível
warn para facilitar a filtragem.
Aplique rate limits a endpoints sensíveis
Estenda RateLimitMiddleware com configurações por rota e registre por meio de middlewareProviders. Aplique com @HttpUseMiddleware em endpoints que lidam com operações caras ou mutações sensíveis, em vez de impor um único limite global.
Valide configurações de confiança em proxy e IP
Se rate limiting ou logging de auditoria depende do IP do cliente, verifique
como seu proxy reverso encaminha x-forwarded-for. Uma configuração de
confiança incorreta pode agrupar usuários sem relação no mesmo bucket ou
permitir que clientes maliciosos falsifiquem sua identidade.
A postura de segurança continua sendo responsabilidade do seu design
O pacote oferece primitivas e padrões seguros, mas decisões finais de política precisam estar explícitas no seu AppModule e na arquitetura de implantação. Revise headers de segurança, origens de CORS e configuração de guard em toda revisão de deploy.