跳转到主要内容

安全

本文档从 KeyDB 的角度介绍了安全主题:涵盖了 KeyDB 提供的访问控制、代码安全问题、通过选择恶意输入可能从外部触发的攻击以及其他类似主题。

有关安全相关事宜,请在 GitHub 上提出问题。

KeyDB 通用安全模型#

KeyDB 被设计为由受信任环境中的受信任客户端访问。这意味着通常不建议将 KeyDB 实例直接暴露给互联网,或者更广泛地说,暴露给不受信任的客户端可以直接访问 KeyDB TCP 端口或 UNIX 套接字的环境。

例如,在一个常见的 Web 应用程序场景中,KeyDB 被用作数据库、缓存或消息系统,应用程序前端(Web 端)的客户端会查询 KeyDB 来生成页面或执行 Web 应用程序用户请求或触发的操作。

在这种情况下,Web 应用程序在 KeyDB 和不受信任的客户端(访问 Web 应用程序的用户浏览器)之间进行中介访问。

这是一个具体的例子,但总的来说,对 KeyDB 的不受信任访问应始终通过一个实现 ACL、验证用户输入并决定对 KeyDB 实例执行何种操作的层来进行中介。

网络安全#

对 KeyDB 端口的访问应该被拒绝给网络中除受信任客户端之外的所有人,因此运行 KeyDB 的服务器应该只允许实现使用 KeyDB 的应用程序的计算机直接访问。

在常见的直接暴露于互联网的单台计算机(如虚拟化的 Linux 实例 Linode, EC2, ...)的情况下,应通过防火墙保护 KeyDB 端口,以防止外部访问。客户端仍然可以使用环回接口访问 KeyDB。

请注意,可以通过在 keydb.conf 文件中添加如下一行来将 KeyDB 绑定到单个接口:

bind 127.0.0.1

由于 KeyDB 的特性,未能保护 KeyDB 端口免受外部攻击可能会产生重大的安全影响。例如,外部攻击者可以使用单个 FLUSHALL 命令删除整个数据集。

保护模式#

不幸的是,许多用户未能保护 KeyDB 实例免受外部网络访问。许多实例只是简单地暴露在具有公共 IP 的互联网上。当 KeyDB 以默认配置(绑定所有接口)运行且没有任何密码访问时,它会进入一个称为保护模式的特殊模式。在此模式下,KeyDB 仅回复来自环回接口的查询,并向从其他地址连接的其他客户端回复错误,解释情况以及如何正确配置 KeyDB。

我们期望保护模式能显著减少因未受保护且未经适当管理的 KeyDB 实例所导致的安全问题,但是系统管理员仍然可以忽略 KeyDB 给出的错误,直接禁用保护模式或手动绑定所有接口。

认证功能#

虽然 KeyDB 不试图实现访问控制,但它提供了一个微小的认证层,可以通过编辑 keydb.conf 文件来选择性地开启。

当启用认证层后,KeyDB 将拒绝任何未经认证的客户端的查询。客户端可以通过发送 AUTH 命令后跟密码来进行自我认证。

密码由系统管理员在 keydb.conf 文件中以明文设置。它应该足够长以防止暴力破解攻击,原因有二:

  • KeyDB 服务查询的速度非常快。外部客户端每秒可以测试许多密码。
  • KeyDB 密码存储在 keydb.conf 文件和客户端配置中,因此系统管理员不需要记住它,所以它可以设置得非常长。

认证层的目标是可选地提供一个冗余层。如果防火墙或任何其他用于保护 KeyDB 免受外部攻击者攻击的系统失效,外部客户端在不知道认证密码的情况下仍然无法访问 KeyDB 实例。

AUTH 命令与所有其他 KeyDB 命令一样,是未加密发送的,因此它无法防止拥有足够网络访问权限以进行窃听的攻击者。

TLS 支持#

KeyDB 在所有通信通道上都提供可选的 TLS 支持,包括客户端连接、复制链接和 KeyDB 集群总线协议。

禁用特定命令#

可以禁用 KeyDB 中的命令,或将其重命名为一个无法猜测的名称,从而将普通客户端限制在一组指定的命令内。

例如,一个虚拟化服务器提供商可能会提供托管的 KeyDB 实例服务。在这种情况下,普通用户可能不应能够调用 KeyDB 的 CONFIG 命令来更改实例的配置,但提供和删除实例的系统应该能够这样做。

在这种情况下,可以从命令表中重命名或完全隐藏命令。此功能可通过在 keydb.conf 配置文件中使用的语句实现。例如:

rename-command CONFIG b840fc02d524045429941cc15f59e41cb7be6c52

在上面的示例中,CONFIG 命令被重命名为一个无法猜测的名称。也可以通过将其重命名为空字符串来完全禁用它(或任何其他命令),如下例所示:

rename-command CONFIG ""

由外部客户端精心选择的输入触发的攻击#

有一类攻击,即使没有对实例的外部访问权限,攻击者也可以从外部触发。这类攻击的一个例子是,向 KeyDB 插入数据,触发 KeyDB 内部实现的数据结构的病态(最坏情况)算法复杂度。

例如,攻击者可以通过 Web 表单提供一组已知哈希到哈希表中同一存储桶的字符串,从而将 O(1) 的预期时间(平均时间)变为 O(N) 的最坏情况,消耗比预期更多的 CPU,最终导致拒绝服务(Denial of Service)。

为防止这种特定攻击,KeyDB 对哈希函数使用了每次执行的伪随机种子。

KeyDB 使用 qsort 算法实现 SORT 命令。目前,该算法不是随机化的,因此可以通过精心选择合适的输入集来触发二次方的最坏情况行为。

字符串转义和 NoSQL 注入#

KeyDB 协议没有字符串转义的概念,因此在正常情况下使用普通客户端库不可能发生注入。该协议使用前缀长度的字符串,并且是完全二进制安全的。

EVALEVALSHA 命令执行的 Lua 脚本遵循相同的规则,因此这些命令也是安全的。

虽然这会是一种非常奇怪的用例,但应用程序应避免使用从不受信任的来源获得的字符串来构建 Lua 脚本的主体。

代码安全#

在经典的 KeyDB 设置中,客户端被允许完全访问命令集,但访问实例绝不应导致能够控制运行 KeyDB 的系统。

在内部,KeyDB 使用所有众所周知的安全编码实践来防止缓冲区溢出、格式化字符串漏洞和其他内存损坏问题。然而,使用 CONFIG 命令控制服务器配置的能力使得客户端能够更改程序的工作目录和转储文件的名称。这允许客户端在随机路径上写入 RDB KeyDB 文件,这是一个安全问题,可能很容易导致系统被攻陷和/或以与 KeyDB 相同的用户身份运行不受信任的代码。

KeyDB 不需要 root 权限来运行。建议以一个专门用于此目的的非特权 *KeyDB* 用户身份运行它。KeyDB 的作者目前正在研究添加一个新的配置参数来阻止 CONFIG SET/GET dir 和其他类似的运行时配置指令的可能性。这将阻止客户端强制服务器在任意位置写入 KeyDB 转储文件。