Segurança

A segurança é fundamental quando trabalhamos com pagamentos. Este guia apresenta as melhores práticas para proteger sua integração.

Proteção da API Key

A API Key é sua credencial de autenticação. Compromissos podem levar a cobranças não autorizadas.

Armazene em Variáveis de Ambiente

Use Cofres de Segredos em Produção

AWS Secrets Manager

    aws secretsmanager create-secret \
      --name toktus-api/api-key \
      --secret-string "tk_a1b2c3d4e5f6789abcdef..."
    ```

    ```javascript
    const AWS = require('aws-sdk');
    const client = new AWS.SecretsManager();

    const secret = await client.getSecretValue({
      SecretId: 'toktus-api/api-key'
    }).promise();

    const apiKey = JSON.parse(secret.SecretString);
    ```

## Azure Key Vault

```bash
    az keyvault secret set \
      --vault-name meu-vault \
      --name toktus-api-api-key \
      --value "tk_a1b2c3d4e5f6789abcdef..."
    ```

    ```javascript
    const { SecretClient } = require('@azure/keyvault-secrets');
    const { DefaultAzureCredential } = require('@azure/identity');

    const client = new SecretClient(
      'https://meu-vault.vault.azure.net',
      new DefaultAzureCredential()
    );

    const secret = await client.getSecret('toktus-api-api-key');
    const apiKey = secret.value;
    ```

## HashiCorp Vault

```bash
    # Armazenar
    vault kv put secret/toktus-api api-key="tk_a1b2c3d4e5f6789abcdef..."

    # Recuperar
    vault kv get -field=api-key secret/toktus-api
    ```

### O que NÃO Fazer

> [!WARNING]
> **Nunca faça nenhuma das seguintes práticas:**
> 
> - Commitar API Keys no Git ou repositórios públicos
> - Hardcodear no código-fonte
> - Expor em requisições client-side (JavaScript no navegador)
> - Compartilhar via e-mail, chat ou canais não criptografados
> - Registrar em logs em texto claro
> - Incluir em URLs ou query parameters

```javascript
// Avoid — hardcoded credentials in source code
const API_KEY = 'tk_a1b2c3d4e5f6789abcdef...';

// Avoid — logging sensitive values in plain text
console.log('API Key:', process.env.TOKTUS_API_API_KEY);

// Correct — redact sensitive values in all log output
console.log('API Key:', '***REDACTED***');

Tokenização de Cartões

NUNCA trafegue dados de cartão através de seu backend sem certificação PCI DSS.

Tokenize no Frontend

Use as bibliotecas JavaScript dos gateways para tokenizar cartões direto no navegador:

Stripe.js

    <script src="https://js.stripe.com/v3/"></script>
    <script>
      const stripe = Stripe('pk_test_sua_chave_publica');
      const elements = stripe.elements();
      const cardElement = elements.create('card');
      cardElement.mount('#card-element');

      // Tokeniza no frontend (nunca envia dados raw ao backend)
      const {token, error} = await stripe.createToken(cardElement);

      if (error) {
        console.error(error.message);
      } else {
        // Envia APENAS o token ao backend
        await fetch('/api/processar-pagamento', {
          method: 'POST',
          body: JSON.stringify({
            cardToken: token.id, // tok_visa
            amount: 10000
          })
        });
      }
    </script>
    ```

## MercadoPago.js

```html
    <script src="https://sdk.mercadopago.com/js/v2"></script>
    <script>
      const mp = new MercadoPago('APP_USR-sua-public-key', {
        locale: 'pt-BR'
      });

      const cardForm = mp.cardForm({
        amount: '100.00',
        iframe: true,
        form: { id: 'form-checkout' },
        callbacks: {
          onSubmit: event => {
            event.preventDefault();
            const {token} = cardForm.getCardFormData();

            fetch('/api/processar-pagamento', {
              method: 'POST',
              body: JSON.stringify({token})
            });
          }
        }
      });
    </script>
    ```

> [!WARNING]
> Nunca envie dados raw de cartão ao backend. Isso viola normas PCI DSS e expõe dados sensíveis.
> 
> ```javascript
> // Avoid — sending raw card data to the backend violates PCI DSS
> fetch('/api/processar-pagamento', {
>   body: JSON.stringify({
>     cardNumber: '4242424242424242',
>     cvv: '123'
>   })
> });
> ```

## SSL/TLS Obrigatório

### Sempre Use HTTPS em Produção

```javascript
// Express - force HTTPS
app.use((req, res, next) => {
  if (req.header('x-forwarded-proto') !== 'https') {
    res.redirect(`https://${req.header('host')}${req.url}`);
  } else {
    next();
  }
});

NGINX com Let's Encrypt

server {
    listen 443 ssl http2;
    server_name api.seudominio.com;

    ssl_certificate /etc/letsencrypt/live/api.seudominio.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/api.seudominio.com/privkey.pem;

    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_prefer_server_ciphers on;

    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;

    location / {
        proxy_pass http://localhost:3001;
    }
}

Validação de Webhooks

Sempre valide que webhooks vieram do gateway legítimo:

Rate Limiting

Implemente rate limiting para proteger contra ataques:

const rateLimit = require('express-rate-limit');

const limiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15 minutos
  max: 100,
  message: {
    success: false,
    error: {
      code: 'RATE_LIMIT_EXCEEDED',
      message: 'Muitas requisições. Tente novamente em 15 minutos.'
    }
  }
});

app.use('/api/', limiter);

Logs e Monitoramento

Registre

  • Tentativas de autenticação (sucesso e falha)
  • Operações de cobrança e estorno
  • Erros de gateway
  • Webhooks recebidos
  • Mudanças de configuração

Nunca Registre

  • Números de cartão completos
  • CVV
  • API Keys em texto claro
  • Tokens não expirados
  • Senhas
  • Dados pessoais sensíveis (CPF completo, endereço)
// Correct — log only non-sensitive identifiers
logger.info('Cobrança criada', {
  chargeId: 'ch_abc123',
  amount: 10000,
  gateway: 'stripe',
  cardLast4: '4242' // Last 4 digits only
});

// Avoid — never log raw card data or credentials
logger.info('Cobrança criada', {
  cardNumber: '4242424242424242',
  apiKey: process.env.API_KEY
});

Criptografia de Dados Sensíveis

Toktus API já criptografa credenciais com AES-256. Se você armazenar dados adicionais, faça o mesmo:

const crypto = require('crypto');

const ENCRYPTION_KEY = process.env.ENCRYPTION_KEY; // 32 bytes
const IV_LENGTH = 16;

function encrypt(text) {
  const iv = crypto.randomBytes(IV_LENGTH);
  const cipher = crypto.createCipheriv(
    'aes-256-cbc', Buffer.from(ENCRYPTION_KEY, 'hex'), iv
  );
  let encrypted = cipher.update(text);
  encrypted = Buffer.concat([encrypted, cipher.final()]);
  return iv.toString('hex') + ':' + encrypted.toString('hex');
}

function decrypt(text) {
  const parts = text.split(':');
  const iv = Buffer.from(parts.shift(), 'hex');
  const encryptedText = Buffer.from(parts.join(':'), 'hex');
  const decipher = crypto.createDecipheriv(
    'aes-256-cbc', Buffer.from(ENCRYPTION_KEY, 'hex'), iv
  );
  let decrypted = decipher.update(encryptedText);
  decrypted = Buffer.concat([decrypted, decipher.final()]);
  return decrypted.toString();
}

Ambiente de Teste

Use ambientes de teste/sandbox antes de produção:

GatewayComo Ativar
StripeUse chaves sk_test_...
Pagar.meUse chaves sk_test_...
AsaasAdicione "sandbox": "true" nas credenciais
Mercado PagoUse credenciais de teste do painel
PagSeguroAdicione "sandbox": "true" nas credenciais

Checklist de Segurança

Antes de ir para produção, verifique:

Recursos Externos