# 集成安全 (/zh/docs/pix-processamento/best-practices/security)

<QuickLinks>
  <QuickLink href="/docs/pix-processamento/authentication" title="认证" />

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

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

Pix 集成涉及真实资金。每一层都必须有自己的防线。

## Token 存储 [#token-存储]

Bearer token 是 PayZu **唯一的凭证**。持有者即可动用余额。

| 存储位置                              | 是否可行？ |
| --------------------------------- | ----- |
| **Google Secret Manager**         | 是     |
| **AWS Secrets Manager**           | 是     |
| **HashiCorp Vault**               | 是     |
| **CI 环境变量**                       | 是     |
| 生产环境的 `.env`                      | 否     |
| 硬编码在代码中                           | 否     |
| `localStorage` / `sessionStorage` | 否     |
| 前端（Web、移动端）                       | 绝不    |

<Callout type="warn">
  **绝不在前端暴露 token。** 所有 PayZu 调用都必须经过你的后端，由服务端注入 `Authorization`。
</Callout>

## 日志脱敏 [#日志脱敏]

配置 logger 对 `Authorization` 头和 payload 中的敏感字段（`payerDocument`、`pixKey` 等）进行脱敏。

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

## Token 轮换 [#token-轮换]

| 何时轮换           | 操作          |
| -------------- | ----------- |
| 有访问权限的人员离职     | 立即          |
| 怀疑泄露（误提交、公开日志） | 立即 + 审计     |
| 预防性轮换          | 每季度或每半年     |
| 渗透测试之后         | 如测试期间已暴露则立即 |

向 PayZu 支持申请轮换。需有**滚动切换计划**（两个 token 在一段窗口内同时有效），以免中断生产。

## Webhook endpoint 保护 [#webhook-endpoint-保护]

仅接受来自 PayZu 官方 IP 的 callback。请向支持索取当前 IP。

<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">
    创建一条 WAF 规则：`(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>

## 付款前 DICT 校验 [#付款前-dict-校验]

按密钥进行 Pix 付款时，付款前通过 DICT 校验持有人。可识别银行/CPF 变更，避免付款到错误的收款人。

```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('pixKey', 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(`持有人不一致：预期 ${expectedName}，实际 ${dict.name}`);
  }

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

详见 [DICT 查询](/docs/pix-processamento/best-practices/dict)。

## 敏感操作的 2FA [#敏感操作的-2fa]

在以下场景之前，考虑**在你的应用中**（而非 PayZu）增加额外的 2FA：

* 高额 Pix 付款。
* 付款 Pix 密钥的注册/变更。
* 具有动用余额权限的管理员登录。

PayZu 后端已做校验，但应用层 2FA 可缩小会话被攻破时的影响面。详见 [2FA](/docs/pix-processamento/two-factor)。

## 最小权限原则 [#最小权限原则]

| 设置              | 建议                            |
| --------------- | ----------------------------- |
| 生产和开发共用同一 token | 分开：sandbox token 与生产 token。   |
| 多个服务共享同一 token  | 每个服务/团队一个 token（便于审计用量）。      |
| 开发人员可访问生产 token | 仅基础设施/SRE 应持有，开发人员使用 sandbox。 |

## 常见陷阱 [#常见陷阱]

| 陷阱                          | 表现                          |
| --------------------------- | --------------------------- |
| Token 提交进 `.env`            | 首次误用 `git push --force` 即泄露 |
| Logger 打印 `Authorization` 头 | 任何日志抓取都会泄露 token            |
| Webhook 向任意 IP 开放           | 伪造 payload 可能被接受            |
| 按密钥 Pix 付款时未做 DICT 校验       | 可能付款到错误的收款人                 |
| sandbox 与生产共用同一 token       | 环境错误演变为生产事故                 |