Noções Básicas de Segurança
8 exemplos para você começar com Segurança para backends Node.js - 6 básicos e 2 intermediários.
Busque em todas as páginas da documentação
8 exemplos para você começar com Segurança para backends Node.js - 6 básicos e 2 intermediários.
npm install zod express@5 helmet
npm install -D typescript@5.6 tsxServiços Node.js enfrentam entradas não confiáveis de corpos HTTP, strings de consulta, cabeçalhos, arquivos enviados, webhooks e variáveis de ambiente carregadas na inicialização.
Rejeite entradas malformadas antes que elas cheguem à lógica de negócios.
import { z } from "zod";
const CreateUserSchema = z.object({
email: z.string().email(),
name: z.string().min(1).max(120),
});
type CreateUser = z.infer<typeof CreateUserSchema>;
function parseCreateUser(body: unknown): CreateUser {
return CreateUserSchema.parse(body);
}parse lança um erro em dados inválidos - mapeie para HTTP 400 no seu manipulador de erros.unknown para body força a validação - nunca confie na tipagem de req.body apenas do middleware.schemas/.Relacionado: OWASP Top 10 para APIs - API1 autorização quebrada no nível do objeto
IDs e filtros da URL são controlados pelo atacante.
import { z } from "zod";
const ListQuerySchema = z.object({
page: z.coerce.number().int().min(1).default(1),
limit: z.coerce.number().int().min(1).max(100).default(20),
});
function parseListQuery(query: Record<string, unknown>) {
return ListQuerySchema.parse(query);
}z.coerce lida com valores de consulta de string do Express/Fastify.limit para evitar DoS via ?limit=999999.Relacionado: Paginação e Filtragem - parâmetros de lista limitados
Chaves de API no histórico do git são para sempre.
import { z } from "zod";
const EnvSchema = z.object({
JWT_SECRET: z.string().min(32),
DATABASE_URL: z.string().url(),
});
export const env = EnvSchema.parse(process.env);parse na inicialização falha as implantações cedo quando os segredos estão faltando..env na imagem.env ou process.env em banners de inicialização.Relacionado: Noções Básicas de Configuração - configuração do doze fatores
Cabeçalhos padrão reduzem o risco de XSS, clickjacking e MIME sniffing.
import express from "express";
import helmet from "helmet";
const app = express();
app.use(helmet());
app.disable("x-powered-by");helmet() define padrões sensatos para cabeçalhos comuns.disable("x-powered-by") remove a identificação do framework.Relacionado: Cabeçalhos de Segurança e CORS - regras CORS de credenciais
Verificações de identidade e permissão são etapas separadas.
type AuthUser = { id: string; role: "admin" | "member" };
function requireAuth(user: AuthUser | undefined): AuthUser {
if (!user) throw new HttpError(401, "Unauthorized");
return user;
}
function requireAdmin(user: AuthUser): void {
if (user.role !== "admin") throw new HttpError(403, "Forbidden");
}user.id === resource.ownerId) em cada acesso ao objeto.user; os manipuladores ainda verificam o acesso no nível do objeto.Relacionado: Regras de Segurança - regras HTTP de linha de base
Rastros de pilha pertencem aos logs, não a respostas JSON.
import express from "express";
import pino from "pino";
class HttpError extends Error {
constructor(public status: number, message: string) {
super(message);
}
}
const logger = pino({ level: "info" });
const app = express();
app.use((err: unknown, req: express.Request, res: express.Response, _next: express.NextFunction) => {
const status = err instanceof HttpError ? err.status : 500;
const message = status < 500 ? (err as Error).message : "Internal Server Error";
logger.error({ err, path: req.path }, "request_error");
res.status(status).json({ error: message });
});err completo com o serializador Pino { err } para rastros de pilha.ZodError do Zod para 400 com caminhos de campo, não mensagens internas.Relacionado: Padrões de Resposta de Erro - erros de API consistentes
Buscar URLs arbitrárias permite o acesso a serviços de metadados internos.
import { lookup } from "node:dns/promises";
import ipaddr from "ipaddr.js";
async function assertPublicUrl(raw: string): Promise<URL> {
const url = new URL(raw);
if (!["http:", "https:"].includes(url.protocol)) throw new Error("Invalid protocol");
const { address } = await lookup(url.hostname);
const parsed = ipaddr.parse(address);
const range = parsed.range();
if (range !== "unicast") throw new Error("Blocked IP range");
return url;
}Relacionado: Guardas SSRF - implementação completa de guarda
A mesclagem profunda de JSON não confiável pode poluir Object.prototype.
function safeAssign<T extends Record<string, unknown>>(
target: T,
source: unknown
): T {
if (source === null || typeof source !== "object" || Array.isArray(source)) {
throw new Error("Invalid merge source");
}
for (const key of Object.keys(source as object)) {
if (key === "__proto__" || key === "constructor" || key === "prototype") continue;
(target as Record<string, unknown>)[key] = (source as Record<string, unknown>)[key];
}
return target;
}Object.assign(target, req.body) sem filtrar chaves em entradas não confiáveis.parse do Zod em vez de mesclagem profunda para moldar requisições.lodash e evite _.merge em JSON do usuário.Relacionado: Poluição de Protótipo - riscos do lodash e merge
Versões da Stack: Esta página foi escrita para Node.js 24.18.0 (LTS Ativo), npm 10+, TypeScript 5.6+, Express 5, Fastify 5 e NestJS 11.
Revisado por Chris St. John·Última atualização: 16 de jul. de 2026