# Segurança (/docs/pix-processamento/best-practices/security)



<QuickLinks>
  <QuickLink href="/docs/pix-processamento/authentication" title="Autenticação" />

  <QuickLink href="/docs/pix-processamento/best-practices/dict" title="Consulta DICT" />

  <QuickLink href="/docs/pix-processamento/two-factor" title="2FA" />
</QuickLinks>

A integração Pix mexe com dinheiro real. Cada camada deve ter sua linha de defesa.

## Armazenamento do token [#armazenamento-do-token]

Bearer token é a **única credencial** da PayZu. Quem tem, movimenta o saldo.

| Onde guardar                      | OK?   |
| --------------------------------- | ----- |
| **Google Secret Manager**         | Sim   |
| **AWS Secrets Manager**           | Sim   |
| **HashiCorp Vault**               | Sim   |
| **Variável de ambiente no CI**    | Sim   |
| `.env` em produção                | Não   |
| Hardcoded no código               | Não   |
| `localStorage` / `sessionStorage` | Não   |
| Front-end (Web, Mobile)           | Nunca |

<Callout type="warn">
  **Nunca exponha o token no front-end.** Toda chamada PayZu deve passar pelo seu backend, que injeta o `Authorization` no servidor.
</Callout>

## Mascaramento em logs [#mascaramento-em-logs]

Configure seu logger para mascarar o header `Authorization` e campos sensíveis do payload (`payerDocument`, `pixKey`, etc).

<Tabs items="['Node.js (pino)', 'Python (logging)', 'Go (zap)']">
  <Tab value="Node.js (pino)">
    ```ts
    import pino from 'pino';

    const logger = pino({
      redact: {
        paths: [
          'req.headers.authorization',
          'res.headers.authorization',
          '*.payerDocument',
          '*.pixKey',
        ],
        censor: '[REDACTED]',
      },
    });
    ```
  </Tab>

  <Tab value="Python (logging)">
    ```python
    import logging, re

    class RedactFilter(logging.Filter):
        def filter(self, record):
            if isinstance(record.msg, str):
                record.msg = re.sub(r'Bearer\s+[A-Za-z0-9._\-]+', 'Bearer [REDACTED]', record.msg)
            return True

    logging.getLogger().addFilter(RedactFilter())
    ```
  </Tab>

  <Tab value="Go (zap)">
    ```go
    logger, _ := zap.NewProduction()
    defer logger.Sync()

    logger.Info("payzu call",
        zap.String("url", url),
        zap.String("authorization", "[REDACTED]"),
    )
    ```
  </Tab>
</Tabs>

## Rotação do token [#rotação-do-token]

| Quando rotacionar                                     | Ação                                    |
| ----------------------------------------------------- | --------------------------------------- |
| Alguém com acesso deixa a empresa                     | Imediato                                |
| Suspeita de vazamento (commit acidental, log público) | Imediato + auditoria                    |
| Rotação preventiva                                    | Trimestral ou semestral                 |
| Após teste de pen-test                                | Imediato se foi exposto durante o teste |

Solicite a rotação ao suporte da PayZu. Tenha **rollover planejado** (dois tokens ativos por uma janela) para não derrubar produção.

## Proteção do endpoint de webhook [#proteção-do-endpoint-de-webhook]

Aceite callbacks apenas do IP oficial da PayZu. Solicite o IP atual ao suporte.

<Tabs items="['Nginx', 'Cloudflare', 'Express']">
  <Tab value="Nginx">
    ```nginx
    location /webhooks/payzu {
      allow 35.199.0.0/16;
      deny all;
      proxy_pass http://backend;
    }
    ```
  </Tab>

  <Tab value="Cloudflare">
    Crie uma WAF rule: `(http.request.uri.path eq "/webhooks/payzu" and ip.src ne <IP_PAYZU>)` → Block.
  </Tab>

  <Tab value="Express">
    ```ts
    const PAYZU_IPS = (process.env.PAYZU_WEBHOOK_IPS ?? '').split(',');

    app.post('/webhooks/payzu', (req, res, next) => {
      const ip = req.ip;
      if (!PAYZU_IPS.includes(ip)) return res.status(403).end();
      next();
    });
    ```
  </Tab>
</Tabs>

## Validação DICT antes de pagar [#validação-dict-antes-de-pagar]

Em saques Pix por chave, valide o titular via DICT antes de transferir. Detecta troca de banco/CPF e evita pagar para destinatário errado.

```ts
async function safeWithdraw(pixKey: string, pixType: string, expectedName: string, amount: number) {
  const url = new URL('https://api.payzu.processamento.com/v1/pix/key');
  url.searchParams.set('key', pixKey);

  const dict = await fetch(url, {
    headers: {
      Authorization: `Bearer ${process.env.PAYZU_TOKEN}`,
      'Content-Type': 'application/json',
    },
  }).then(r => r.json());

  if (dict.name?.toLowerCase().trim() !== expectedName.toLowerCase().trim()) {
    throw new Error(`Titular divergente: esperado ${expectedName}, encontrado ${dict.name}`);
  }

  return createWithdraw({ pixKey, pixType, amount });
}
```

Detalhes em [Consulta DICT](/docs/pix-processamento/best-practices/dict).

## 2FA em operações sensíveis [#2fa-em-operações-sensíveis]

Considere 2FA adicional **na sua aplicação** (não na PayZu) antes de:

* Saque acima de um limite alto.
* Cadastro/alteração de chave Pix de saque.
* Login admin com permissão de movimentar saldo.

A PayZu já valida no backend, mas 2FA aplicacional reduz o blast radius de uma sessão comprometida. Detalhes em [2FA](/docs/pix-processamento/two-factor).

## Princípio do menor privilégio [#princípio-do-menor-privilégio]

| Setup                                    | Recomendação                                   |
| ---------------------------------------- | ---------------------------------------------- |
| Token único pra prod + dev               | Separe: token de sandbox vs token de prod.     |
| Mesmo token compartilhado entre serviços | Um token por serviço/equipe (pra auditar uso). |
| Devs com acesso ao token de prod         | Só infra/SRE deveria ter, devs usam sandbox.   |

## Armadilhas comuns [#armadilhas-comuns]

| Armadilha                             | Sintoma                                    |
| ------------------------------------- | ------------------------------------------ |
| Token em `.env` versionado            | Vaza no primeiro `git push --force` errado |
| Logger imprime header `Authorization` | Token vaza em qualquer captura de log      |
| Webhook aberto para qualquer IP       | Payloads forjados podem ser aceitos        |
| Pagar sem DICT em saque por chave     | Pode pagar destinatário errado             |
| Mesmo token em sandbox e produção     | Erro de ambiente vira incidente em prod    |
