你的云数据库是 Security Theater

发布: (2026年1月15日 GMT+8 14:03)
6 min read
原文: Dev.to

Source: Dev.to

传统多租户 SaaS 存储的问题

多租户 SaaS 存储在架构层面上根本存在安全隐患。供应商承诺行级安全、每个查询都带有租户 ID,以及静态加密,但一次 SQL 注入或特权提升漏洞就可能暴露 所有 租户的数据。一次泄露可能影响成千上万的受害者。

传统 SaaS 架构

┌─────────────────────────────────┐
│      Application Layer          │
│  (trusted, handles isolation)   │
└─────────────────────────────────┘

┌─────────────────────────────────┐
│  Database (ALL tenant data)     │
│  WHERE tenant_id = ?            │
└─────────────────────────────────┘
  • 一个缺失的 WHERE 子句、一个被攻破的 API 端点,或一个 IDOR 漏洞都可能危及每个租户。
  • “静态加密”并不奏效,因为加密密钥与提供查询的系统位于同一处。它只能防止物理盗窃,无法阻止拥有数据库访问权限的攻击者。

Source:

BSFS:存储层的密码学隔离

BSFS(块存储文件系统)消除了应用内部的共享数据库和信任边界。如果没有密钥,数据不存在

核心概念

  • 无共享数据库 – 每个租户使用其独立的加密分区。
  • 密码学隔离 – 元数据和数据使用租户专属密钥加密;无需访问控制检查。

通过加密块分配表(BAT)的原子提交

  1. 分配随机块地址。
  2. 将新文件数据写入这些块。
  3. 更新加密的 BAT。
  4. 将旧块标记为空闲。

BAT 的更新提交点。如果进程在写入过程中崩溃,读取者仍然看到旧文件;新块则成为垃圾。没有损坏、没有部分写入、也没有“最终一致性”的胡说。

// The atomic commit point
bsfs_encrypt_bat(partition_key, bat, encrypted_bat);
fseek(blob_file, partition_offset, SEEK_SET);
fwrite(encrypted_bat, sizeof(bsfs_bat_t), 1, blob_file);
fflush(blob_file);
// Transaction committed. Readers see new file.
  • fwrite 之前崩溃 → 旧文件保持完整。
  • fwrite 之后崩溃 → 新文件已提交。

密钥派生

每个租户获得一个主密钥。分区密钥通过 HKDF 派生:

Master Key → HKDF(partition_id) → Partition Key
  • 主密钥可以派生任意分区密钥。
  • 分区密钥不能派生兄弟密钥。

这提供了真正的密码学隔离,而不仅仅是访问控制检查。

随机块分配

BSFS 故意使用 Fisher‑Yates 洗牌随机化块的放置。文件在分区内被散布,防止攻击者通过块访问模式推断文件结构或文件之间的关系。

什么是 BSFS(以及不是)

不是替代品

  • PostgreSQL 或任何关系型数据库
  • 分布式文件系统
  • 键‑值存储
  • 为数百万小文件或范围查询优化的系统

预期使用场景

  • 真正的租户隔离
  • 原子文件更新
  • 加密元数据
  • 为 SaaS 应用存储提供可预测的性能

可以把它视为 加密对象存储(例如具有加密边界的 S3 桶),而不是带有 tenant_id 列的行。

API 示例

C 示例

uint8_t master_key[32];
bsfs_tenant_t tenant;

bsfs_tenant_init(&tenant, "storage.blob", master_key);
bsfs_write_file(&tenant, file_id, data, size);
bsfs_read_file(&tenant, file_id, &data, &size);
bsfs_delete_file(&tenant, file_id);
bsfs_tenant_cleanup(&tenant);

Python 示例

from bsfs import BSFS, generate_master_key
import uuid

master_key = generate_master_key()
with BSFS('storage.blob', master_key) as fs:
    file_id = uuid.uuid4()
    fs.write_file(file_id, b'confidential data')
    data = fs.read_file(file_id)
    fs.delete_file(file_id)

操作限制

  • 每个分区最多 64 个文件
  • 最大文件大小 512 MiB(2 MiB 块 × 256 块)
  • 单分区实现(计划支持多分区)
  • 不支持流式 I/O —— 整个文件加载到内存中
  • 单线程

何时使用 BSFS

  • 构建多租户 SaaS 产品
  • 合规监管要求严格的数据隔离
  • 过去遇到行级安全漏洞的经验
  • 需要对文件、文档或二进制大对象(blob)进行可证明的加密边界

何时不使用 BSFS

  • 需要关系查询或复杂连接
  • 存储数百万条小记录
  • 需要分布式共识或高可用复制
  • 威胁模型 包括数据库泄露

成本比较

云数据库会对 IOPS、存储和计算进行收费。BSFS 运行在本地 NVMe 上,使用原始磁盘 I/O,并仅对元数据进行加密。您可以避免:

  • 数据库连接开销
  • 查询优化器运行时
  • 复制延迟
  • “无服务器”附加费用
  • 每租户数据库实例

单个 2 TB NVMe 硬盘即可为数十个租户提供真正的密码学隔离,其成本甚至低于每月一个托管 RDS 实例的费用。

结论

大多数 SaaS 的安全性是通过访问控制列表——社会惯例来强制执行的。BSFS 使跨租户数据访问 在密码学上不可能,而不仅仅是未授权的。这在经历了数十年的妥协捷径后,回归了第一原理。

进一步资源

  • 视频演练(实现、写时复制语义、加密元数据)
  • 源代码仓库:
Back to Blog

相关文章

阅读更多 »

Rapg:基于 TUI 的密钥管理器

我们都有这种经历。你加入一个新项目,首先听到的就是:“在 Slack 的置顶消息里查找 .env 文件”。或者你有多个 .env …

技术是赋能者,而非救世主

为什么思考的清晰度比你使用的工具更重要。Technology 常被视为一种魔法开关——只要打开,它就能让一切改善。新的 software,...

踏入 agentic coding

使用 Copilot Agent 的经验 我主要使用 GitHub Copilot 进行 inline edits 和 PR reviews,让我的大脑完成大部分思考。最近我决定 t...