7. Segurança
A segurança é um aspecto crítico do nosso processo de desenvolvimento. Nosso objetivo é estar em conformidade com o OWASP Top 10 e implementar as melhores práticas de segurança em todos os níveis de nossa aplicação.
7.1. Práticas de codificação segura
A01:2021 - Broken Access Control
Descrição: Falhas que permitem que usuários acessem recursos ou realizem ações além de suas permissões.
Como evitar:
- Implementar controle de acesso em cada camada da aplicação (apresentação, lógica de negócios, dados).
- Negar acesso por padrão, exceto para recursos públicos.
- Implementar mecanismos de controle de acesso uma vez e reutilizá-los em toda a aplicação.
Exemplo (Laravel):
public function update(Request $request, $id)
{
$user = User::findOrFail($id);
// Verificar se o usuário atual tem permissão para atualizar este usuário
if (!$request->user()->can('update', $user)) {
abort(403);
}
// Prosseguir com a atualização
}Exemplo (Node.js/Express):
const checkPermission = (requiredRole) => {
return (req, res, next) => {
if (req.user && req.user.role === requiredRole) {
next();
} else {
res.status(403).json({ error: 'Acesso negado' });
}
};
};
app.put('/users/:id', checkPermission('admin'), (req, res) => {
// Lógica de atualização do usuário
});A02:2021 - Cryptographic Failures
Descrição: Falhas relacionadas à criptografia que podem levar à exposição de dados sensíveis.
Como evitar:
- Classificar dados processados, armazenados ou transmitidos pela aplicação.
- Não armazenar dados sensíveis desnecessariamente.
- Criptografar todos os dados sensíveis em repouso.
- Usar algoritmos e protocolos criptográficos fortes e atualizados.
Exemplo (PHP):
// Criptografar dados sensíveis antes de armazenar
$encryptedData = openssl_encrypt($sensitiveData, 'AES-256-CBC', $encryptionKey, 0, $iv);
// Descriptografar dados
$decryptedData = openssl_decrypt($encryptedData, 'AES-256-CBC', $encryptionKey, 0, $iv);Exemplo (Node.js):
const crypto = require('crypto');
const algorithm = 'aes-256-cbc';
const key = crypto.randomBytes(32);
const iv = crypto.randomBytes(16);
function encrypt(text) {
let cipher = crypto.createCipheriv(algorithm, Buffer.from(key), iv);
let encrypted = cipher.update(text);
encrypted = Buffer.concat([encrypted, cipher.final()]);
return { iv: iv.toString('hex'), encryptedData: encrypted.toString('hex') };
}
function decrypt(text) {
let iv = Buffer.from(text.iv, 'hex');
let encryptedText = Buffer.from(text.encryptedData, 'hex');
let decipher = crypto.createDecipheriv(algorithm, Buffer.from(key), iv);
let decrypted = decipher.update(encryptedText);
decrypted = Buffer.concat([decrypted, decipher.final()]);
return decrypted.toString();
}A03:2021 - Injection
Descrição: Falhas que permitem a injeção de código malicioso em consultas SQL, comandos de sistema, etc.
Como evitar:
- Usar consultas parametrizadas ou prepared statements.
- Validar e sanitizar todas as entradas do usuário.
- Usar ORMs (Object-Relational Mapping) com consultas parametrizadas.
Exemplo (Laravel - prevenção de SQL Injection):
$users = DB::table('users')
->where('status', '=', 'active')
->whereIn('id', $safeIds)
->get();Exemplo (Node.js/Express - prevenção de XSS):
const express = require('express');
const helmet = require('helmet');
const app = express();
// Usar Helmet para configurar vários cabeçalhos HTTP de segurança
app.use(helmet());
// Sanitizar entrada do usuário
const sanitizeHtml = require('sanitize-html');
app.post('/comment', (req, res) => {
const sanitizedComment = sanitizeHtml(req.body.comment);
// Salvar comentário sanitizado
});A04:2021 - Insecure Design
Descrição: Riscos relacionados a falhas no design da aplicação.
Como evitar:
- Implementar modelagem de ameaças no processo de design.
- Usar padrões de design seguros e princípios de segurança.
- Desenvolver e usar bibliotecas e frameworks que implementam controles de segurança.
Exemplo (Modelagem de Ameaças):
- Identificar ativos (ex: dados de usuários, sistemas de pagamento)
- Criar um diagrama de fluxo de dados
- Identificar ameaças usando o modelo STRIDE (Spoofing, Tampering, Repudiation, Information Disclosure, Denial of Service, Elevation of Privilege)
- Classificar e priorizar ameaças
- Desenvolver estratégias de mitigação
A05:2021 - Security Misconfiguration
Descrição: Configurações de segurança inadequadas ou padrão.
Como evitar:
- Implementar um processo de hardening para remover configurações desnecessárias.
- Revisar e atualizar configurações regularmente.
- Implementar segmentação de aplicação para separar componentes.
Exemplo (PHP - configuração segura do PHP.ini):
display_errors = Off
log_errors = On
error_log = /path/to/error.log
allow_url_fopen = Off
allow_url_include = Off
session.cookie_httponly = 1
session.cookie_secure = 1Exemplo (Node.js - configuração segura do Express):
const helmet = require('helmet');
app.use(helmet());
app.use(helmet.contentSecurityPolicy({
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'", "'unsafe-inline'"],
styleSrc: ["'self'", "'unsafe-inline'"],
imgSrc: ["'self'", "data:", "https:"],
},
}));A06:2021 - Vulnerable and Outdated Components
Descrição: Uso de componentes com vulnerabilidades conhecidas.
Como evitar:
- Manter um inventário atualizado de todos os componentes e suas versões.
- Monitorar continuamente fontes como CVE e NVD para vulnerabilidades.
- Usar ferramentas de análise de composição de software.
Exemplo (PHP - usando Composer):
# Verificar dependências vulneráveis
composer audit
# Atualizar dependências
composer updateExemplo (Node.js - usando npm):
# Verificar vulnerabilidades
npm audit
# Corrigir vulnerabilidades
npm audit fix
# Atualizar dependências
npm updateA07:2021 - Identification and Authentication Failures
Descrição: Falhas nos mecanismos de identificação e autenticação.
Como evitar:
- Implementar autenticação multi-fator.
- Implementar senhas fortes e políticas de rotação de senhas.
- Limitar ou atrasar tentativas de login repetidas.
Exemplo (Laravel - autenticação multi-fator):
use Laravel\Fortify\TwoFactorAuthenticatable;
class User extends Authenticatable
{
use TwoFactorAuthenticatable;
// ...
}Exemplo (Node.js - limitação de taxa de login):
const rateLimit = require("express-rate-limit");
const loginLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutos
max: 5 // limite de 5 tentativas
});
app.post("/login", loginLimiter, (req, res) => {
// Lógica de login
});A08:2021 - Software and Data Integrity Failures
Descrição: Falhas relacionadas à integridade de software e dados.
Como evitar:
- Usar assinaturas digitais para verificar a integridade de software.
- Garantir que dependências e bibliotecas venham de repositórios confiáveis.
- Garantir que os processos de CI/CD sejam seguros.
Exemplo (Verificação de integridade de arquivo):
$file = 'important_data.txt';
$hash = hash_file('sha256', $file);
// Verificar se o hash corresponde ao hash conhecido
if ($hash !== $knownHash) {
throw new Exception('Integridade do arquivo comprometida');
}A09:2021 - Security Logging and Monitoring Failures
Descrição: Falhas na logging e monitoramento de segurança.
Como evitar:
- Garantir que todos os erros de login, controle de acesso e validação de entrada do lado do servidor possam ser registrados.
- Garantir que os logs sejam gerados em um formato que possa ser facilmente consumido por soluções de gerenciamento de logs.
- Garantir que logs de transações de alto valor tenham uma trilha de auditoria.
Exemplo (Laravel - logging):
Log::channel('security')->info('Tentativa de login', [
'user' => $request->input('email'),
'ip' => $request->ip(),
'user_agent' => $request->userAgent()
]);Exemplo (Node.js - logging com Winston):
const winston = require('winston');
const logger = winston.createLogger({
level: 'info',
format: winston.format.json(),
defaultMeta: { service: 'user-service' },
transports: [
new winston.transports.File({ filename: 'error.log', level: 'error' }),
new winston.transports.File({ filename: 'combined.log' })
]
});
// Exemplo de uso
logger.info('Tentativa de login', {
user: req.body.email,
ip: req.ip,
userAgent: req.headers['user-agent']
});A10:2021 - Server-Side Request Forgery (SSRF)
Descrição: Vulnerabilidades que permitem que um atacante faça solicitações do servidor para recursos internos ou externos não autorizados.
Como evitar:
- Sanitizar e validar todas as entradas de dados fornecidas pelo usuário.
- Implementar listas de permissão para URLs e IPs permitidos.
- Não enviar respostas brutas para clientes.
- Desativar redirecionamentos HTTP.
Exemplo (PHP - prevenção de SSRF):
function isUrlAllowed($url) {
$allowedDomains = ['api.example.com', 'api.trusteddomain.com'];
$parsedUrl = parse_url($url);
return in_array($parsedUrl['host'], $allowedDomains);
}
$url = $request->input('url');
if (!isUrlAllowed($url)) {
throw new Exception('URL não permitida');
}
$response = Http::get($url);Exemplo (Node.js - prevenção de SSRF):
const url = require('url');
function isUrlAllowed(urlString) {
const allowedDomains = ['api.example.com', 'api.trusteddomain.com'];
const parsedUrl = url.parse(urlString);
return allowedDomains.includes(parsedUrl.hostname);
}
app.get('/fetch-data', (req, res) => {
const requestUrl = req.query.url;
if (!isUrlAllowed(requestUrl)) {
return res.status(403).send('URL não permitida');
}
// Prosseguir com a solicitação
});7.2. Gerenciamento de dependências e vulnerabilidades
O gerenciamento adequado de dependências é crucial para manter a segurança de nossa aplicação. Seguiremos estas práticas:
-
Uso de ferramentas de análise de dependências:
- Snyk: Integraremos o Snyk em nosso pipeline de CI/CD.
# Instalar Snyk CLI npm install -g snyk # Autenticar com Snyk snyk auth # Testar o projeto snyk test # Monitorar o projeto continuamente snyk monitor
- Snyk: Integraremos o Snyk em nosso pipeline de CI/CD.
-
Atualização regular de dependências:
-
Para projetos Node.js:
# Verificar atualizações npm outdated # Atualizar pacotes npm update # Atualizar para versões principais mais recentes (com cuidado) npm install <package-name>@latest -
Para projetos PHP/Laravel:
# Verificar atualizações composer outdated # Atualizar pacotes composer update
-
-
Revisão de vulnerabilidades:
- Configurar alertas do GitHub para notificações de vulnerabilidades.
- Revisar semanalmente o relatório de dependências do GitHub.
- Agendar uma revisão mensal completa de todas as dependências.
-
Política de versionamento:
- Usar versionamento semântico (SemVer) para todas as dependências.
- Fixar versões de dependências em
package.jsonoucomposer.json.
Exemplo (package.json):
"dependencies": { "express": "^4.17.1", "lodash": "^4.17.21" } -
Processo de atualização:
- Criar um ambiente de teste para validar atualizações.
- Executar suite de testes completa após cada atualização.
- Documentar todas as atualizações e seus impactos.
7.3. Autenticação e autorização
A implementação correta de autenticação e autorização é fundamental para a segurança da aplicação, para qualquer nova aplicacao, deverá ser utilizado algum protocolo conhecido para implementar tal funcionalidade, como Supabase Auth, Keycloak, AWS Cognito, Ory e etc.
-
Autenticação forte com PassPort:
-
Implementar OAuth 2.0 e OpenID Connect:
Exemplo usando Passport.js (Node.js):
const passport = require('passport'); const GoogleStrategy = require('passport-google-oauth20').Strategy; passport.use(new GoogleStrategy({ clientID: GOOGLE_CLIENT_ID, clientSecret: GOOGLE_CLIENT_SECRET, callbackURL: "http://www.example.com/auth/google/callback" }, function(accessToken, refreshToken, profile, cb) { User.findOrCreate({ googleId: profile.id }, function (err, user) { return cb(err, user); }); } )); -
Para Laravel, usar Laravel Passport:
use Laravel\Passport\HasApiTokens; class User extends Authenticatable { use HasApiTokens, Notifiable; }
-
-
Tokens JWT para autenticação stateless:
- Todo e qualquer produto novo deverá ter JWT em sua primeira versao publica.
-
Controle de acesso baseado em funçes (RBAC):
- Definir roles (ex: 'admin', 'user', 'editor').
- Implementar middleware para verificar permissões
-
Autenticação de dois fatores (2FA):
- Todo e qualquer produto novo deverá ter 2FA em sua primeira versao publica.
-
Políticas de senha:
- Exigir senhas fortes (mínimo 12 caracteres, combinação de letras, números e símbolos).
- Implementar verificação de senha contra listas de senhas comuns.
- Usar algum algoritmo forte disponivel para a linguagem escolhida, sem utilizar criptografias baixas, como md5, sha1 e etc.
7.4. Criptografia e proteção de dados
A proteção adequada dos dados é essencial para manter a confiança dos usuários e cumprir regulamentações.
-
HTTPS para todas as comunicações:
-
Configurar HTTPS no servidor web:
Para Nginx:
server { listen 443 ssl; server_name example.com; ssl_certificate /path/to/certificate.crt; ssl_certificate_key /path/to/certificate.key; # Configurações de segurança adicionais ssl_protocols TLSv1.2 TLSv1.3; ssl_prefer_server_ciphers on; ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384; } -
Redirecionar todo o tráfego HTTP para HTTPS:
server { listen 80; server_name example.com; return 301 https://$server_name$request_uri; }
-
-
Criptografia em repouso:
-
Usar criptografia de disco completo para servidores.
-
Para dados sensíveis no banco de dados:
Exemplo usando o módulo
cryptodo Node.js:const crypto = require('crypto'); const algorithm = 'aes-256-cbc'; const key = crypto.randomBytes(32); const iv = crypto.randomBytes(16); function encrypt(text) { let cipher = crypto.createCipheriv(algorithm, Buffer.from(key), iv); let encrypted = cipher.update(text); encrypted = Buffer.concat([encrypted, cipher.final()]); return { iv: iv.toString('hex'), encryptedData: encrypted.toString('hex') }; } function decrypt(text) { let iv = Buffer.from(text.iv, 'hex'); let encryptedText = Buffer.from(text.encryptedData, 'hex'); let decipher = crypto.createDecipheriv(algorithm, Buffer.from(key), iv); let decrypted = decipher.update(encryptedText); decrypted = Buffer.concat([decrypted, decipher.final()]); return decrypted.toString(); }
-
-
Funçes de hash seguras para senhas:
- Usar algum algoritmo forte disponivel para a linguagem escolhida, sem utilizar criptografias baixas, como md5, sha1 e etc.
-
Gerenciamento seguro de chaves:
- Usar um serviço de gerenciamento de chaves (KMS) como AWS KMS ou HashiCorp Vault.
- Nunca armazenar chaves diretamente no código ou em arquivos de configuração não criptografados.
-
Proteção contra ataques de canal lateral:
- Usar comparações de tempo constante para tokens e hashes:
const crypto = require('crypto'); function safeCompare(a, b) { return crypto.timingSafeEqual(Buffer.from(a), Buffer.from(b)); }
- Usar comparações de tempo constante para tokens e hashes:
7.5. Conformidade com LGPD
A conformidade com a Lei Geral de Proteção de Dados (LGPD) é crucial para operar legalmente no Brasil.
-
Consentimento explícito:
- Implementar um sistema de opt-in claro
- Armazenar registros de consentimento por toda a vida
-
Acesso, correção e exclusão de dados:
- Implementar endpoints para estas operações
-
Registros de processamento de dados:
- Manter logs detalhados de todas as operaçes de processamento
-
Medidas técnicas e organizacionais:
- Implementar controle de acesso granular
- Criptografar dados sensíveis
-
Notificação de violações de dados:
- Implementar um sistema de detecção e notificação
- Caso algum dado tenha sido acessado com um issuer diferente, os administradores deverao ser notificados;
-
Política de retenção de dados:
- Implementar um sistema de exclusão automática de dados antigos;
- Os dados precisam ser criptografados em repouso com criptografia forte;
- O prazo maximo de retencao será de 3 anos, sendo por padrao de 6 meses;
7.6. Criptografia e proteção de dados
A proteção adequada dos dados é essencial para manter a confiança dos usuários e cumprir regulamentações.
-
HTTPS para todas as comunicações:
-
Configurar HTTPS no servidor web:
Para Nginx:
server { listen 443 ssl; server_name example.com; ssl_certificate /path/to/certificate.crt; ssl_certificate_key /path/to/certificate.key; # Configurações de segurança adicionais ssl_protocols TLSv1.2 TLSv1.3; ssl_prefer_server_ciphers on; ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384; } -
Redirecionar todo o tráfego HTTP para HTTPS:
server { listen 80; server_name example.com; return 301 https://$server_name$request_uri; }
-
-
Criptografia em repouso:
-
Usar criptografia de disco completo para servidores.
-
Para dados sensíveis no banco de dados:
Exemplo usando o módulo
cryptodo Node.js:const crypto = require('crypto'); const algorithm = 'aes-256-cbc'; const key = crypto.randomBytes(32); const iv = crypto.randomBytes(16); function encrypt(text) { let cipher = crypto.createCipheriv(algorithm, Buffer.from(key), iv); let encrypted = cipher.update(text); encrypted = Buffer.concat([encrypted, cipher.final()]); return { iv: iv.toString('hex'), encryptedData: encrypted.toString('hex') }; } function decrypt(text) { let iv = Buffer.from(text.iv, 'hex'); let encryptedText = Buffer.from(text.encryptedData, 'hex'); let decipher = crypto.createDecipheriv(algorithm, Buffer.from(key), iv); let decrypted = decipher.update(encryptedText); decrypted = Buffer.concat([decrypted, decipher.final()]); return decrypted.toString(); }
-
-
Funções de hash seguras para senhas:
- Usar algum algoritmo forte disponível para a linguagem escolhida, sem utilizar criptografias baixas, como md5, sha1 e etc.
-
Gerenciamento seguro de chaves:
- Usar um serviço de gerenciamento de chaves (KMS) como AWS KMS, HashiCorp Vault ou Infisical.
- Nunca armazenar chaves diretamente no código ou em arquivos de configuração não criptografados.
-
Proteção contra ataques de canal lateral:
- Usar comparações de tempo constante para tokens e hashes:
const crypto = require('crypto'); function safeCompare(a, b) { return crypto.timingSafeEqual(Buffer.from(a), Buffer.from(b)); }
- Usar comparações de tempo constante para tokens e hashes:
-
Armazenamento criptografado de documentos sensíveis:
- Todo documento que contiver dados sensíveis deverá ser armazenado criptografado.
- O acesso a estes documentos deverá ser feito somente com link assinado (considerando o uso de serviços como S3, R2, etc.).
Exemplo de implementação usando AWS SDK para Node.js:
const AWS = require('aws-sdk'); const s3 = new AWS.S3(); // Função para gerar um link assinado function getSignedUrl(bucket, key) { const params = { Bucket: bucket, Key: key, Expires: 60 * 5 // URL expira em 5 minutos }; return s3.getSignedUrlPromise('getObject', params); } // Uso getSignedUrl('my-secure-bucket', 'path/to/sensitive-document.pdf') .then(url => console.log('URL assinada:', url)) .catch(err => console.error('Erro:', err)); -
Geração de arquivos com senha:
- Todos os arquivos gerados devem ser protegidos com senha.
- A senha deverá ser os 5 primeiros dígitos do CNPJ/CPF do contratante, sem qualquer caractere extra além dos numéricos.
Exemplo de implementação usando a biblioteca
pdf-libpara Node.js:const { PDFDocument } = require('pdf-lib'); const fs = require('fs').promises; async function createProtectedPDF(content, password) { const pdfDoc = await PDFDocument.create(); const page = pdfDoc.addPage(); page.drawText(content); // Aplicar senha ao documento const pdfBytes = await pdfDoc.save({ password: password, userPassword: password, ownerPassword: password }); await fs.writeFile('protected-document.pdf', pdfBytes); } // Uso const cnpjCpf = '12345678000190'; // CNPJ exemplo const password = cnpjCpf.slice(0, 5); // Pega os 5 primeiros dígitos createProtectedPDF('Conteúdo sensível do documento', password) .then(() => console.log('PDF protegido criado com sucesso')) .catch(err => console.error('Erro ao criar PDF:', err));