Diretrizes de Desenvolvimento
7. Segurança

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):

  1. Identificar ativos (ex: dados de usuários, sistemas de pagamento)
  2. Criar um diagrama de fluxo de dados
  3. Identificar ameaças usando o modelo STRIDE (Spoofing, Tampering, Repudiation, Information Disclosure, Denial of Service, Elevation of Privilege)
  4. Classificar e priorizar ameaças
  5. 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 = 1

Exemplo (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 update

Exemplo (Node.js - usando npm):

# Verificar vulnerabilidades
npm audit
 
# Corrigir vulnerabilidades
npm audit fix
 
# Atualizar dependências
npm update

A07: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:

  1. 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
  2. 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
  3. 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.
  4. Política de versionamento:

    • Usar versionamento semântico (SemVer) para todas as dependências.
    • Fixar versões de dependências em package.json ou composer.json.

    Exemplo (package.json):

    "dependencies": {
      "express": "^4.17.1",
      "lodash": "^4.17.21"
    }
  5. 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.

  1. 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;
      }
  2. Tokens JWT para autenticação stateless:

    • Todo e qualquer produto novo deverá ter JWT em sua primeira versao publica.
  3. Controle de acesso baseado em funçes (RBAC):

    • Definir roles (ex: 'admin', 'user', 'editor').
    • Implementar middleware para verificar permissões
  4. Autenticação de dois fatores (2FA):

    • Todo e qualquer produto novo deverá ter 2FA em sua primeira versao publica.
  5. 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.

  1. 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;
      }
  2. Criptografia em repouso:

    • Usar criptografia de disco completo para servidores.

    • Para dados sensíveis no banco de dados:

      Exemplo usando o módulo crypto do 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();
      }
  3. 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.
  4. 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.
  5. 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));
      }

7.5. Conformidade com LGPD

A conformidade com a Lei Geral de Proteção de Dados (LGPD) é crucial para operar legalmente no Brasil.

  1. Consentimento explícito:

    • Implementar um sistema de opt-in claro
    • Armazenar registros de consentimento por toda a vida
  2. Acesso, correção e exclusão de dados:

    • Implementar endpoints para estas operações
  3. Registros de processamento de dados:

    • Manter logs detalhados de todas as operaçes de processamento
  4. Medidas técnicas e organizacionais:

    • Implementar controle de acesso granular
    • Criptografar dados sensíveis
  5. 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;
  6. 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.

  1. 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;
      }
  2. Criptografia em repouso:

    • Usar criptografia de disco completo para servidores.

    • Para dados sensíveis no banco de dados:

      Exemplo usando o módulo crypto do 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();
      }
  3. 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.
  4. 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.
  5. 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));
      }
  6. 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));
  7. 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-lib para 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));