Segurança indie: 5 camadas que seu SaaS precisa (sem overkill)
Guia prático de segurança pra solopreneur. Quais defesas você realmente precisa, quanto custam e onde não vale investir tempo agora.
Por Vitor Morais
Fundador do MochaLabz ·
Segurança infraestrutura solopreneur indie não é sobre ter nível militar de defesa. É sobre fechar as portas por onde 80% dos ataques realmente entram — e ignorar o resto. Em 2026, empresas pequenas sofreram aumento de 38% em tentativas de invasão, segundo dados recentes; criminosos usam robôs que testam milhões de conexões buscando falhas básicas como senhas fracas e servidores desatualizados. Este artigo mapeia as 5 camadas críticas que seu MVP em Next.js + Supabase/PostgreSQL precisa, quanto cada uma custa em tempo e dinheiro, e sinceramente — qual você pode ignorar por enquanto.
Camada 1: Senha forte + controle de acesso (30 min, R$ 0)
Começa aqui. Senhas fracas ou reutilizadas são o vetor de ataque número um contra indie hackers. Não é paranoia — é estatística. Se sua aplicação permite login com 123456 ou a mesma senha funciona em email + app, você não tem defesa de verdade, só ilusão.
- No seu app: Force
minLength: 12caracteres, exija mix de maiúsculas, números e símbolos (!@#$%). Teste comzxcvbn(biblioteca de estimativa de força de senha) pra rejeitarpassword123antes de salvar. - No banco: Nunca armazene senha em plaintext. Hash com
bcrypt(cost fator ≥ 12) ouargon2. Se usa Supabase, a funçãoauth.usersjá vem hasheada com bcrypt. - No seu acesso admin: Credenciais do banco (Supabase role, SSH key) vivem em
.env.local— nunca em git, nunca em Slack. Rotacione a chave SSH a cada 6 meses. - Tabela de permissões: Se seu SaaS tem admin/user/viewer, force no backend com
if (user.role !== 'admin')antes de qualquer operação sensível. Confiar só no frontend é garantia de vazamento.
lib/auth.ts — Hash seguro com bcrypt
import bcrypt from 'bcrypt';
export async function hashPassword(password: string) {
return bcrypt.hash(password, 12);
}
export async function verifyPassword(password: string, hash: string) {
return bcrypt.compare(password, hash);
}
// Validação no endpoint
export function validatePasswordStrength(pwd: string) {
if (pwd.length < 12) return false;
if (!/[A-Z]/.test(pwd)) return false; // maiúscula
if (!/[0-9]/.test(pwd)) return false; // número
if (!/[!@#$%^&*]/.test(pwd)) return false; // símbolo
return true;
}Não invente sua própria criptografia
Se você pensa em usar MD5, SHA1 sem salt ou qualquer outra coisa que viu num post de 2015, pare. Use bcrypt ou argon2. Ponto.
Camada 2: Autenticação multi-fator (MFA) — 1h, grátis ou R$ 30/mês
Depois que senha está forte, próximo passo é MFA. Mesmo que alguém descobra sua senha, sem acesso ao celular (ou segundo fator), não entra. Para solopreneur, a prioridade é proteger sua própria conta admin — depois expande pra clientes pagos.
- TOTP (Time-based One-Time Password): Livre, simples. Usuário escaneia QR code com Google Authenticator, Authy ou similar, gera código de 6 dígitos a cada 30 seg. Validação no backend com
speakeasyou similar. - SMS/Email: Funciona, mas menos seguro (SIM swap, email hijack). Se escolher, use serviço como Twilio (SMS) ou Resend (email code). Custo: ~R$ 0,10/SMS.
- Passkeys (WebAuthn): Futuro — sem senha, sem TOTP, só biometria ou chave de segurança. Suporte crescente em 2026. Considere
@simplewebauthnse vender premium.
api/mfa/enable.ts — Gerar QR code TOTP
import speakeasy from 'speakeasy';
import QRCode from 'qrcode';
export async function generateTOTPSecret(userId: string, email: string) {
const secret = speakeasy.generateSecret({
name: `YourApp (${email})`,
issuer: 'YourApp',
length: 32,
});
const qr = await QRCode.toDataURL(secret.otpauth_url);
return {
secret: secret.base32,
qrCode: qr,
};
}
export function verifyTOTPToken(secret: string, token: string) {
return speakeasy.totp.verify({
secret: secret,
encoding: 'base32',
token: token,
window: 2, // ±2 períodos (60 segundos de tolerância)
});
}A implementação típica: usuário clica "ativar MFA", recebe QR code, escaneia, digita código de teste pra confirmar que funciona, e pronto — próximo login pede TOTP. Supabase tem suporte nativo — use se estiver nele.
Camada 3: Updates e patches automáticos — 20 min setup, crucial
Em 2026, a descoberta de vulnerabilidades em dependências acelerou. O curl (ferramenta ubíqua) registrou 87 CVEs em apenas 9 dias de abril. Se sua app roda Next.js, Prisma, pg (driver PostgreSQL) ou qualquer outra dependency, e você não atualiza há 3 meses, é questão de tempo até uma vulnerability explorar seu servidor.
- Setup automático: Configure Dependabot (GitHub) ou Renovate (agnóstico) pra abrir PRs de patch automáticas todo dia/semana. Ele roda testes, você aprova em 30 seg.
- Política: Minor/patch updates (1.2.3 → 1.2.4) merge direto. Major (1.0 → 2.0) gera PR pra você revisar.
- Monitoramento: Inscreva-se na newsletter de segurança do projeto (Next.js, Prisma, Supabase). Demora 5 min configurar, economiza horas depois.
- Supabase/banco: Se usa Supabase, eles patcheam PostgreSQL automaticamente. Se roda Postgres self-hosted, cron job de update mensal é obrigatório.
.github/dependabot.yml — Setup básico
version: 2
updates:
- package-ecosystem: 'npm'
directory: '/'
schedule:
interval: 'weekly'
allow:
- dependency-type: 'production'
- dependency-type: 'direct'
reviewers:
- 'seu-github-user'
auto-merge:
enabled: true
rules:
- base-branch-pattern: 'main'
dependency-type-string: '(patch|minor)' # automat aprova patch/minor
Renovate vs Dependabot
Dependabot vem de graça no GitHub. Renovate é mais poderoso (agrupa updates, automerge smarter) — não vale a pena pra indie small, fica GitHub nativo.
Camada 4: Backup testado + disaster recovery — 2h setup, R$ 10–50/mês
Dados é seu ativo. Se banco corrompe, é ransomware ou você deleta acidentalmente a tabela errada, backup é única salvação. Nem todo backup funciona — precisa testar restauração regularmente.
- Supabase: Vem com snapshots automáticos diários (até 30 dias grátis). Seu custo é o plano base. Teste restauração uma vez por mês — não leva 20 min.
- PostgreSQL self-hosted:
pg_dumpem cron job (todo dia 2am), armazena em S3 (R$ 0,023 por GB/mês) ou Backblaze B2 (mais barato). FerramentabarmanoupgBackRestautomizam. - Arquivo: Pelo menos 1 backup offline (download local) a cada 3 meses. Se hacker encripta tudo em nuvem, backup local desconectado ainda funciona.
- Teste: Mensalmente, rode
pg_restoreem banco vazio e valide que dados vieram corretos. Se não testa, quando precisar vai descobrir que backup está quebrado.
scripts/backup.sh — Backup automático PostgreSQL
#!/bin/bash
# Cron: 0 2 * * * /home/app/scripts/backup.sh
DB_HOST="localhost"
DB_USER="postgres"
DB_NAME="meu_saas"
BACKUP_DIR="/backups"
S3_BUCKET="s3://meu-bucket-backups"
DATE=$(date +%Y%m%d_%H%M%S)
# Dump local
pg_dump -h $DB_HOST -U $DB_USER $DB_NAME | gzip > "$BACKUP_DIR/backup_$DATE.sql.gz"
# Upload S3
aws s3 cp "$BACKUP_DIR/backup_$DATE.sql.gz" "$S3_BUCKET/"
# Limpa backups locais com >30 dias
find $BACKUP_DIR -name "backup_*.sql.gz" -mtime +30 -delete
Disaster recovery também inclui: documentação de como restaurar (markdown com passos), chave SSH de acesso ao servidor salva em lugar seguro (não em email), lista de clients importados/clientes críticos que precisam notificação se downtime.
Camada 5: Isolamento de dados (multi-tenant) e validação de input — 3h code, impede 90% dos hacks
Se você vende SaaS pra múltiplos clientes num único banco, isolamento de dados é crítico — cliente A não pode ver dados de cliente B por um typo seu. E se aceita input do usuário (formulário, API), todo input é hostil até prova contrária — SQL injection, XSS, path traversal.
- Isolamento de tenant: Toda query inclui
WHERE tenant_id = ?. Parametrize (prepared statements) — nunca concatene string. Prisma já faz isso. - Validação de input: Use
zodouajvpra validar tipo, tamanho, formato antes de processar.POST /api/userrecebe JSON? Valide que{ name: string, email: string }, rejeita se vier{ name: [1,2,3] }. - Sanitização: Não confie em browser — sanitize no backend. Se salva HTML, use
DOMPurifyousanitize-htmlpra remover scripts. - Rate limiting: Proteja endpoints contra brute force.
express-rate-limitno Node, ou Cloudflare Workers Rate Limiting. Limite a 10 tentativas de login por IP por 15 min.
api/posts/[id].ts — Isolamento seguro de tenant
import { prisma } from '@/lib/prisma';
import { getSession } from '@/lib/auth';
import { z } from 'zod';
const updatePostSchema = z.object({
title: z.string().min(3).max(200),
content: z.string().min(10),
});
export async function PATCH(req: Request, { params }: { params: { id: string } }) {
const session = await getSession(req);
if (!session) return new Response('Unauthorized', { status: 401 });
// Validação de input
const data = updatePostSchema.parse(await req.json());
// Garante que post pertence ao tenant logado
const post = await prisma.post.findFirst({
where: {
id: params.id,
tenantId: session.tenantId, // Linha crítica
},
});
if (!post) return new Response('Not found', { status: 404 });
const updated = await prisma.post.update({
where: { id: post.id },
data,
});
return Response.json(updated);
}
| Camada | Tempo setup | Custo mensal | Impacto (% ataques bloqueados) | Crítico? |
|---|---|---|---|---|
| Senha + controle | 30 min | R$ 0 | 25% | SIM |
| MFA | 1h | R$ 0–30 | 35% | Sim (seu admin) |
| Updates automáticos | 20 min | R$ 0 | 20% | SIM |
| Backup testado | 2h | R$ 10–50 | Recuperação de dados | SIM |
| Isolamento + validação | 3h code | R$ 0 | 15% | SIM |
Percentuais são aproximados
Não existe fórmula exata. Atacantes usam vetor mais fácil — se você fecha senhas fracas, tentam exploit de dependency; se patches estão atualizados, tentam SQL injection, etc. As 5 camadas combinadas reduzem área de ataque em ~80%.
O que NÃO vale investir agora (e por quê)
- WAF (Web Application Firewall): CloudFlare pro, AWS WAF. Útil se sofres ataques DDoS específicos — até lá, Cloudflare grátis + rate limiting é bastante.
- Penetration testing: Contrata especialista pra tentar invadir seu app. Custa R$ 5k–20k. Só vale se tem clientes enterprise pedindo SOC 2 — antes disso, não faz ROI.
- Sistema de monitoramento 24/7 (SIEM): Splunk, Datadog security. Para 1 pessoa, logs do provider (Vercel, Supabase) bastam — review semanalmente.
- Criptografia em repouso: Dados já estão em HTTPS em trânsito; se banco é Supabase, já criptografa disco. Self-hosted? Criptografia de disco do SO (LVM, LUKS) é grátis.
- Chave de segurança de hardware: Yubikey, Nitrokey. Paranoia pura até ter 6 clientes pagos — depois, sim, investe na sua conta.
Regra de ouro: proteja onde o dano real aconteceria. Se perder dados, cliente sai. Se levam 1h downtime, sobrevive. Invista accordingly.
Checklist de implementação (copy-paste agora)
- [ ] Password no banco usa bcrypt (cost ≥ 12). Teste:
bcrypt.compare('password', hash)deve retornar true. - [ ] Validação de força de senha no signup. Rejeita <12 caracteres ou sem maiúscula/número/símbolo.
- [ ] Role-based access control: admin, user, viewer. Backend valida
user.roleantes de CRUD. - [ ] Dependabot ativado. Recebe PRs de security patches todo dia.
- [ ] TOTP MFA configurado para seu próprio admin. Authy ou Google Authenticator funciona.
- [ ] Backup automático testado. Consegue restaurar de verdade em <30 min.
- [ ] Todas as queries Prisma/SQL têm
WHERE tenant_id = ?. Nenhuma query expõe dados de outro cliente. - [ ] Validação Zod/AJV em todos os endpoints POST/PUT. Rejeita tipos errados.
- [ ] Rate limiting em login: max 10 tentativas por IP por 15 min.
- [ ]
.env.localnunca foi commited em git..gitignoretem*.env.local,*.env.production.
Gerador de política de privacidade (LGPD)
Se seu SaaS coleta dados de clientes, precisa de privacy policy. Gere em 5 min compliant com LGPD (Brasil).
Gerar política →Perguntas frequentes
Se uso Supabase, preciso me preocupar com segurança?+
Supabase cuida de segurança de infraestrutura (DDoS, encriptação, backup). Você é responsável por senha forte, MFA, validação de input no seu código, isolamento de tenant no SQL. É partnership — ambos fazem sua parte.
Qual é a melhor prática pra armazenar API keys de terceiros (Stripe, SendGrid)?+
Em variáveis de ambiente (.env.production em produção, secrets manager no Vercel). Nunca em código, nunca em localStorage. Rotacione keys a cada 6–12 meses ou se suspeit vazamento.
Ransomware apagou meu banco — como recupero?+
Se tem backup offline testado, restaura em <1h. Se não tem, foi. Por isso backup é crítico — não é opcional, é seguro de vida do SaaS.
Quanto tempo gasto por mês mantendo segurança?+
30 min reviewando Dependabot PRs, 15 min checando logs Supabase, 1h testando backup. Total ~2h/mês. Muito menos que tempo perdido em breach.
Preciso de firewall ou VPN pra proteger meu SaaS?+
Não. HTTPS + validação de input + updates automáticos + isolamento de tenant cobrem 95%. Firewall é pra rede corporativa, não pra indie SaaS público.
Como saber se fui atacado?+
Vercel/Supabase logs mostram requisições suspeitas (10k POST /api/login em 1 min = brute force). Rate limiting previne; se ver, analisa log, bloqueia IP e rotaciona senha. Ferramentas como Axiom (logs) detalham mais.
Artigos relacionados
Como Criar Sitemap XML (2026): Guia Completo com Geração Automática
Guia definitivo de sitemap XML: estrutura, campos obrigatórios, sitemap-index, geração dinâmica em Next.js e WordPress, envio ao Search Console e erros comuns.
LGPD para Sites: Checklist Completo de Adequação (2026)
Guia LGPD para sites brasileiros: 30+ itens de checklist, bases legais explicadas, multas reais aplicadas pela ANPD, prioridade de implementação por fase e ferramentas recomendadas.
Banner de Cookies LGPD: Como Implementar Corretamente em 2026
Guia completo: opt-in granular, categorias certas, Google Consent Mode v2, implementação em Next.js e WordPress, registro de consentimento, dark patterns proibidos e ferramentas pagas vs open source.
UUID v7 no PostgreSQL: quando migrar e ganhar 2x de performance
Guia prático: por que UUID v7 é melhor que v4, impacto real em índices B-tree, como migrar sem downtime e quando vale a pena.