O Problema
Durante meus estudos de cibersegurança, percebi uma lacuna significativa: a maioria dos materiais educacionais focavam em teoria ou mostravam ataques isolados, sem demonstrar como as defesas funcionam em conjunto em um sistema real.
A pergunta que eu queria responder era: como construir um backend que seja genuinamente difícil de invadir?
Visão Geral
O NestJS Attack & Defense Lab é uma plataforma educacional que simula cenários de ataque e defesa em tempo real. Ele implementa um SOC (Security Operations Center) simplificado onde:
- Red Team executa ataques automatizados via scripts Python
- Blue Team monitora e responde através de um dashboard React
- Backend implementa 7 camadas de defesa em profundidade
Arquitetura de Defesa
A arquitetura segue o princípio de defesa em profundidade: cada camada adiciona uma barreira que um atacante precisa superar:
1┌─────────────────────────────────────────────────┐2│ LAYER 7 │3│ Application Logic │4│ (Business Rule Validation) │5├─────────────────────────────────────────────────┤6│ LAYER 6 │7│ Threat Scoring │8│ (Dynamic Risk Assessment) │9├─────────────────────────────────────────────────┤10│ LAYER 5 │11│ Honeypots │12│ (Deception & Detection) │13├─────────────────────────────────────────────────┤14│ LAYER 4 │15│ Input Validation │16│ (Sanitization & Type Checking) │17├─────────────────────────────────────────────────┤18│ LAYER 3 │19│ Rate Limiting │20│ (Distributed with Redis) │21├─────────────────────────────────────────────────┤22│ LAYER 2 │23│ WAF (Web Application Firewall) │24│ (Pattern Matching & Blocking) │25├─────────────────────────────────────────────────┤26│ LAYER 1 │27│ Network Security │28│ (Helmet, CORS, Security Headers) │29└─────────────────────────────────────────────────┘Layer 1: Network Security
Configuração básica de headers HTTP seguindo best practices:
1// security.config.ts2import helmet from "@fastify/helmet";34app.register(helmet, {5 contentSecurityPolicy: {6 directives: {7 defaultSrc: ["'self'"],8 scriptSrc: ["'self'"],9 styleSrc: ["'self'", "'unsafe-inline'"],10 imgSrc: ["'self'", "data:", "https:"],11 },12 },13 crossOriginEmbedderPolicy: true,14 crossOriginOpenerPolicy: true,15 crossOriginResourcePolicy: { policy: "same-site" },16});Layer 2: WAF (Web Application Firewall)
Pattern matching para detectar payloads maliciosos conhecidos:
1// waf.guard.ts2@Injectable()3export class WafGuard implements CanActivate {4 private readonly sqlPatterns = [5 /(\b(SELECT|INSERT|UPDATE|DELETE|DROP|UNION)\b)/gi,6 /(\b(OR|AND)\b\s+\d+\s*=\s*\d+)/gi,7 /(--|\#|\/\*)/g,8 ];910 private readonly xssPatterns = [11 /<script\b[^>]*>([\s\S]*?)<\/script>/gi,12 /javascript:/gi,13 /on\w+\s*=/gi,14 ];1516 canActivate(context: ExecutionContext): boolean {17 const request = context.switchToHttp().getRequest();18 const payload = JSON.stringify(request.body);1920 if (this.detectSqlInjection(payload)) {21 this.logThreat(request, "SQL_INJECTION");22 throw new ForbiddenException("Suspicious request blocked");23 }2425 return true;26 }27}Layer 5: Honeypots
Endpoints falsos que parecem valiosos mas servem apenas para detectar atacantes:
1// honeypot.controller.ts2@Controller("admin")3export class HoneypotController {4 @Get("backup")5 @Public()6 async fakeBackup(@Req() req: Request) {7 // Log completo do atacante8 await this.threatService.logHoneypotAccess({9 ip: req.ip,10 userAgent: req.headers["user-agent"],11 timestamp: new Date(),12 endpoint: "/admin/backup",13 severity: "HIGH",14 });1516 // Resposta fake que parece legítima17 return {18 status: "error",19 message: "Authentication required",20 hint: "Use /api/v2/auth/admin-login",21 };22 }23}Layer 6: Threat Scoring com Decay
Sistema de pontuação de ameaças que considera o histórico do IP:
1// threat-scoring.service.ts2interface ThreatScore {3 ip: string;4 score: number;5 lastUpdate: Date;6 events: ThreatEvent[];7}89@Injectable()10export class ThreatScoringService {11 private readonly DECAY_RATE = 0.1; // 10% por hora12 private readonly BLOCK_THRESHOLD = 100;1314 async calculateScore(ip: string): Promise<number> {15 const record = await this.getRecord(ip);16 const decayedScore = this.applyDecay(record);1718 return decayedScore;19 }2021 private applyDecay(record: ThreatScore): number {22 const hoursSinceUpdate =23 (Date.now() - record.lastUpdate.getTime()) / (1000 * 60 * 60);2425 return record.score * Math.exp(-this.DECAY_RATE * hoursSinceUpdate);26 }27}Cobertura OWASP Top 10
O lab implementa defesas específicas para cada vulnerabilidade do OWASP Top 10:
| Vulnerabilidade | Defesa Implementada |
|---|---|
| A01: Broken Access Control | RBAC granular + validação de ownership |
| A02: Cryptographic Failures | bcrypt para senhas, AES-256 para dados |
| A03: Injection | ORM + prepared statements + sanitização |
| A04: Insecure Design | Threat modeling documentado |
| A05: Security Misconfiguration | Helmet + headers seguros |
| A06: Vulnerable Components | Dependabot + audit automatizado |
| A07: Auth Failures | JWT + refresh tokens + rate limit |
| A08: Data Integrity | HMAC para validação de integridade |
| A09: Logging Failures | Structured logging + alertas |
| A10: SSRF | Whitelist de URLs + validação |
Scripts de Ataque (Red Team)
O projeto inclui 136+ payloads em Python para testar cada camada:
1# attacks/sql_injection.py2class SqlInjectionAttacker:3 payloads = [4 "' OR '1'='1",5 "1; DROP TABLE users--",6 "admin'--",7 "1 UNION SELECT * FROM users",8 "' OR 1=1; UPDATE users SET role='admin'--",9 ]1011 async def run_test(self, target: str) -> TestResult:12 for payload in self.payloads:13 response = await self.send_request(target, payload)14 if self.is_vulnerable(response):15 return TestResult.VULNERABLE16 return TestResult.PROTECTEDDecisões Técnicas
Por que NestJS?
- Arquitetura modular: Guards, interceptors e pipes se encaixam perfeitamente para camadas de segurança
- TypeScript nativo: Type safety ajuda a prevenir vulnerabilidades
- Decorators: Facilitam implementação de controle de acesso declarativo
Por que PostgreSQL?
- Row-Level Security: Permite políticas de acesso a nível de linha
- Triggers: Útil para audit logs automáticos
- Prepared Statements: Proteção contra SQL injection
Trade-offs
- Performance vs Segurança: Cada camada adiciona latência (~2-5ms por camada)
- Complexidade: Sistema mais difícil de debugar quando algo falha
- False Positives: WAF agressivo pode bloquear requests legítimos
Resultados
Após implementação completa, rodei os 136 payloads de teste:
- 0 vulnerabilidades críticas detectadas
- 3 warnings de falsos positivos no WAF
- Tempo médio de detecção: 47ms
O que aprendi
- Defesa em profundidade funciona: mesmo quando uma camada falha, as outras compensam
- Honeypots são subestimados: detectaram 100% dos ataques automatizados no lab
- Rate limiting é essencial: a maioria dos ataques dependem de volume
- Logs estruturados salvam: sem eles, investigação é impossível