实时安全

由您的会话规则决定 谁可以订阅。

无需在 Bird 中配置任何权限模型。私有或在线状态订阅由您编写的端点审批,使用您已有的会话,并用只有您的服务器持有的密钥进行签名。Bird 验证签名;策略由您决定。

auth.ts
200 · signed
// Your endpoint. The only place the app secret lives.
app.post("/bird/auth", async (req, res) => {
  const { connection_id, channel_name } = req.body;
  const user = await session(req);

  if (!mayJoin(user, channel_name)) return res.sendStatus(403);

  // Signs <connection_id>:<channel_name>[:<member_data>] with the app
  // secret, and adds shared_secret on an encrypted channel.
  res.json(
    await bird.realtime.authorizeChannel({
      connectionId: connection_id,
      channelName: channel_name,
      memberData: JSON.stringify({
        member_id: user.id,
        member_info: { name: user.name },
      }),
    }),
  );
});

一个密钥是公开的,另一个不是。

一切源于这个区分。

Bird Realtime API 上的每个应用都有一个密钥和一个密钥密文。密钥用于嵌入客户端代码;密钥密文用于验证服务器调用和签名订阅,且仅在创建时显示一次。任何持有它的人都可以向您的应用发布消息并伪造在线身份,因此请像对待数据库密码一样妥善保管。密钥轮换是叠加式而非中断式的:创建第二个密钥,部署后再撤销旧密钥。

四项控制,四个问题

谁可以订阅,谁可以保持连接,谁可以读取消息负载,谁仍然被允许访问。

  1. 01

    签名订阅。

    客户端将其连接 ID 和通道名称发送到您的端点。您验证调用者身份,如果不允许加入则返回 403 拒绝,或返回对连接 ID 和通道名称的 HMAC-SHA256 签名(以应用密钥为前缀)。由于连接 ID 包含在签名中,一次授权仅覆盖一个连接,无法在另一个连接上重放。在线状态通道还会签名成员身份,且必须是您返回的完全一致的字符串:对同一对象重新序列化可能会重新排列键名并使签名失效。

  2. 02

    授权连接。

    应用密钥是公开的,因此任何能加载您页面的人都可以用它打开一个 WebSocket 连接。开启授权连接后,每个新连接有 30 秒时间证明持有密钥密文的一方为其担保——通过私有订阅或登录。未完成验证的连接将以代码 4009 关闭,且未授权连接不计入您的配额。订阅公共通道不能证明任何身份,也不会授权连接。

  3. 03

    端到端加密。

    private-encrypted- 通道在请求离开您的进程之前由服务器密封,使用一个永远不会出现在 Realtime API 请求中的 32 字节主密钥。每个通道派生自己的密钥,因此授权客户端访问一个加密通道并不意味着它能读取另一个。Bird 无法恢复丢失的密钥,密钥轮换保护的是未来的消息负载而非过去的。

  4. 04

    立即撤销访问权限。

    用户登出、修改密码、账户被封禁:断开该成员的连接,该身份持有的所有连接——在所有设备上——都将关闭。客户端将此关闭视为终态而不会重试,且您的端点停止为其签名,因此他们无法再次连接。

加密通道

Bird 无法读取的消息负载,运行在 Bird 管理的基础设施上。

服务器 SDK 识别通道前缀,从您的主密钥派生该通道的密钥,并在本地密封消息负载。边缘节点转发密文,您的授权端点仅将派生密钥分发给已批准的客户端。设计时需考虑两个限制:通道名称和事件名称以明文传输,因此选择不会泄露所保护内容的名称;加密不能与在线状态或客户端事件组合使用。缓存可以这样实现:private-encrypted-cache- 通道以密封状态存储其缓存事件。

encrypted.ts
sealed locally
const bird = new BirdClient({
  apiKey: process.env.BIRD_API_KEY!,
  realtime: {
    key: process.env.BIRD_REALTIME_KEY,
    secret: process.env.BIRD_REALTIME_SECRET,
    // 32 random bytes, yours alone. Never sent to Bird.
    encryptionMasterKey: process.env.BIRD_REALTIME_MASTER_KEY,
  },
});

// Sealed in your process. The edge forwards ciphertext.
await bird.realtime.publish(APP_ID, {
  event: "order.updated",
  channels: ["private-encrypted-orders"],
  data: { order_id: "ord_123", status: "shipped" },
});

授权不能做什么。

要求授权连接控制的是谁可以保持 WebSocket 连接打开,而不是谁可以读取某个通道:公共通道对每个已授权连接仍然可读,因此属于单个客户的事件应放在私有通道中,由您的端点检查通道名称。由于您的端点是权威来源,一个过于宽松的端点和泄露的密钥一样会大量分发访问权限。客户端事件同理——边缘节点不做校验:仅将其用于信号,任何需要权威性的内容都应通过您的服务器路由。

数据存储位置。

应用在创建时选择其所在区域,且终生不变,因此请选择离您用户最近的区域;应用的区域可以与您工作区的主区域不同。应用也是隔离边界:两个应用永远看不到彼此的通道,这正是每个环境使用一个应用、让测试流量远离生产环境的最佳方式。

在文档中深入了解。

授权通道包含请求和响应的契约以及需要签名的确切字符串。要求授权连接涵盖 30 秒窗口和代码 4009,加密通道涵盖密钥生成和轮换,终止成员连接是登录和断开的流程。

付诸实践。

继续查阅此主题的文档、指南和示例。资源为英文。

动手实践并获取实施简报

传递密钥,守住密钥。

签名订阅、授权连接和加密频道是每个 Realtime 应用的标准配置,适用于所有套餐。

从一个渠道开始。
准备好后,再添加其他渠道。

测试 API 密钥即刻可用。添加支付方式并验证发送者身份后,即可解锁生产环境。

正在使用 Claude Code、Cursor 或 Codex?复制一条设置提示,您的智能代理即可自动安装 Bird CLI 和相关技能。选择您的工具:

Cursor