Pular para o conteúdoPedro Farbo
Lição 20 / 2545 min

Segurança de APIs

Segurança da API

Proteger sua API contra ataques é fundamental em produção.

Helmet - Headers de Segurança

bash
npm install helmet
typescript
// src/app.tsimport helmet from 'helmet'; app.use(helmet()); // Configuração personalizadaapp.use(helmet({  contentSecurityPolicy: {    directives: {      defaultSrc: ["'self'"],      styleSrc: ["'self'", "'unsafe-inline'"],      scriptSrc: ["'self'"],      imgSrc: ["'self'", "data:", "https:"],    },  },  hsts: {    maxAge: 31536000,    includeSubDomains: true,  },}));

CORS Configurado

typescript
// src/config/cors.tsimport cors from 'cors'; const allowedOrigins = [  'http://localhost:3000',  'https://ecommerce.com',  'https://admin.ecommerce.com',]; export const corsConfig = cors({  origin: (origin, callback) => {    if (!origin || allowedOrigins.includes(origin)) {      callback(null, true);    } else {      callback(new Error('Bloqueado pelo CORS'));    }  },  methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'],  allowedHeaders: ['Content-Type', 'Authorization'],  credentials: true,  maxAge: 86400, // 24 horas});

Sanitização de Input

bash
npm install express-mongo-sanitize xss-clean
typescript
import mongoSanitize from 'express-mongo-sanitize';import xss from 'xss-clean'; // Remove caracteres $ e . (NoSQL injection)app.use(mongoSanitize()); // Sanitiza input contra XSSapp.use(xss());

Validação Robusta com Zod

typescript
// src/schemas/user.schema.tsimport { z } from 'zod'; export const registerSchema = z.object({  body: z.object({    name: z.string()      .min(2, 'Nome muito curto')      .max(100, 'Nome muito longo')      .regex(/^[a-zA-ZÀ-ú\s]+$/, 'Nome inválido'),     email: z.string()      .email('E-mail inválido')      .toLowerCase()      .max(255),     password: z.string()      .min(8, 'Mínimo 8 caracteres')      .regex(/[A-Z]/, 'Precisa de letra maiúscula')      .regex(/[a-z]/, 'Precisa de letra minúscula')      .regex(/[0-9]/, 'Precisa de número')      .regex(/[^A-Za-z0-9]/, 'Precisa de caractere especial'),  }),});

Rate Limiting Avançado

typescript
// src/middlewares/rate-limit.middleware.tsimport rateLimit from 'express-rate-limit';import RedisStore from 'rate-limit-redis';import redis from '../config/redis'; // Rate limit geralexport const generalLimiter = rateLimit({  windowMs: 15 * 60 * 1000, // 15 minutos  max: 100,  message: { error: 'Muitas requisições' },  standardHeaders: true,  legacyHeaders: false,  store: new RedisStore({    sendCommand: (...args: string[]) => redis.call(...args),  }),}); // Rate limit para auth (mais restrito)export const authLimiter = rateLimit({  windowMs: 60 * 60 * 1000, // 1 hora  max: 5, // 5 tentativas  message: { error: 'Muitas tentativas de login' },  skipSuccessfulRequests: true,}); // Usoapp.use('/api', generalLimiter);app.use('/api/auth/login', authLimiter);

Proteção contra Brute Force

typescript
// src/services/auth.service.tsimport redis from '../config/redis'; const MAX_ATTEMPTS = 5;const BLOCK_DURATION = 15 * 60; // 15 minutos export class AuthService {  async login(email: string, password: string) {    const attemptsKey = `login_attempts:${email}`;    const blockKey = `login_blocked:${email}`;     // Verifica se está bloqueado    const isBlocked = await redis.get(blockKey);    if (isBlocked) {      throw new AppError('Conta temporariamente bloqueada', 429);    }     const user = await prisma.user.findUnique({ where: { email } });    const isValid = user && await bcrypt.compare(password, user.password);     if (!isValid) {      const attempts = await redis.incr(attemptsKey);      await redis.expire(attemptsKey, BLOCK_DURATION);       if (attempts >= MAX_ATTEMPTS) {        await redis.setex(blockKey, BLOCK_DURATION, '1');        await redis.del(attemptsKey);        throw new AppError('Conta bloqueada por 15 minutos', 429);      }       throw new AppError('Credenciais inválidas', 401);    }     // Reset tentativas no sucesso    await redis.del(attemptsKey);     return this.generateToken(user);  }}

Logging de Segurança

typescript
// src/middlewares/security-logger.middleware.tsimport { Request, Response, NextFunction } from 'express'; export const securityLogger = (req: Request, res: Response, next: NextFunction) => {  const suspiciousPatterns = [    /(\%27)|(\')|(\-\-)|(\%23)|(#)/i, // SQL Injection    /<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, // XSS    /\.\.\//g, // Path traversal  ];   const checkValue = (value: string): boolean => {    return suspiciousPatterns.some(pattern => pattern.test(value));  };   const allParams = {    ...req.query,    ...req.body,    ...req.params,  };   for (const [key, value] of Object.entries(allParams)) {    if (typeof value === 'string' && checkValue(value)) {      console.warn('⚠️ Tentativa suspeita detectada:', {        ip: req.ip,        path: req.path,        param: key,        value: value.substring(0, 100),        timestamp: new Date().toISOString(),      });    }  }   next();};

Variáveis de Ambiente Seguras

typescript
// src/config/env.tsimport { z } from 'zod'; const envSchema = z.object({  NODE_ENV: z.enum(['development', 'production', 'test']),  DATABASE_URL: z.string().url(),  JWT_SECRET: z.string().min(32),  // ... outras variáveis}); export const env = envSchema.parse(process.env);

Resumo

  • ✅ Helmet para headers de segurança
  • ✅ CORS configurado corretamente
  • ✅ Sanitização contra injeções
  • ✅ Rate limiting com Redis
  • ✅ Proteção contra brute force
  • ✅ Logging de segurança

Próxima aula: Docker e Containers! 🐳

Gostou do conteúdo? Sua contribuição ajuda a manter tudo online e gratuito!

PIX:0737160d-e98f-4a65-8392-5dba70e7ff3e