为什么我不能让客户直接连接到我的数据库?

信息安全 账户安全 mysql api
2021-08-23 00:19:00

我很确定这是一个愚蠢的想法,但我想知道为什么,所以请耐心等待。
后端开发人员所做的许多工作是通过 HTTP 向客户提供 CRUD 访问,本质上是将数据从内部数据库映射到内部数据库。客户通过加密连接使用某种凭据向 Web 服务授权,Web 服务验证数据并对后端数据库执行查询,然后将结果返回给客户端。

总而言之,这只是直接与数据库交互的一种更糟糕的方式:几乎没有人完全实现 REST 规范,而且迟早你总是会使用自制的通用过滤、排序或分页——而 SQL 支持所有这些已经。

这让我想知道:为什么不让客户通过暴露 SQL 端口来访问数据库,完全跳过 HTTP API?这有很多优点:

  • 客户端必须使用客户端证书加密连接
  • 我们可以使用服务器内置的访问控制,或者只使用每个客户的分片数据库
  • (My-)SQL 权限非常细粒度,所以我敢打赌不应该有任何明显的安全问题
  • 性能应该会更好,因为我们跳过了整个 HTTP 通信和 Web 应用程序代码
  • 新功能是数据库迁移的问题,一切都反映在架构中
  • 为用户提供强大的查询能力,无需任何额外的努力

缺点似乎包括无法支持多个模式版本,尽管我认为谨慎的弃用(可能还有客户端 SDK)应该使影响最小化。

由于似乎没有人这样做,因此我必须忽略安全风险。为什么我们不能为客户提供公共 SQL 访问?什么可能出错?(请记住,这只是一个出于好奇而产生的思想实验)

4个回答

TL,DR:不要。

(My-)SQL 权限非常细粒度,所以我敢打赌不应该有任何明显的安全问题

即使获得了记录级别的许可,它也不容易扩展。如果用户SELECT对某个表进行了无限制,他们可以选择该表上的任何记录,甚至是那些不属于他们的记录。薪水表会很糟糕。如果任何用户有DELETEor UPDATE,他们可能会忘记该WHERE子句,然后您的桌子就到了。它甚至发生在 DBA 身上,那么为什么它不会发生在用户身上呢?

性能应该会更好,因为我们跳过了整个 HTTP 通信和 Web 应用程序代码

并且您抛弃了所有安全、审计、过滤和真正细粒度的权限控制,不再使用应用程序来验证、过滤、授予和拒绝访问。而且通常花费在事务上的大部分时间是数据库处理查询。应用程序代码少于此,您不会删除 HTTP 通信,只需将其替换为 SQL 通信即可。

新功能是数据库迁移的问题,一切都反映在架构中

这就是为什么这么多人使用“电子表格作为数据库”的原因。当您需要协调来自多个来源的数据时,这是一场噩梦。

为用户提供强大的查询能力,无需任何额外的努力

这就像把一个强大的引擎放在骨架底盘上,用螺栓固定在座位上,然后带着它去参加比赛。没有额外的重量使汽车减速,所以它非常快!

这里也一样。当然,它快速而强大,但没有应用程序提供的安全措施、没有会话、记录级访问控制、“用户做他们允许做的事”或审计。

Web 应用程序中最常见的漏洞之一是 SQL 注入,您正在为用户提供一个 SQL 控制台。你给了他们各种各样的枪,很多子弹,还有你的脚,你的手,你的头……他们中的一些人不喜欢你。

有趣的问题。理论上,这可以安全地完成。MS-SQL 可以通过加密保护连接、验证用户身份,并提供细粒度的权限和其他安全功能,如审计。

事实上,以前在内网环境中,胖客户端直接访问数据库是很常见的,所以数据库安全控制是主要的安全机制。这往往做得很糟糕,例如,所有用户都以管理员身份使用应用程序中硬编码的密码进行连接。但它可以做得很好。

一个主要问题是权限提升缺陷。数据库 API 极其复杂,存在巨大的攻击面,并且协议是为速度而设计的,并且是旧的并且没有对 Internet 进行加固。例如,甲骨文就有数百个权限提升漏洞。但是,MS-SQL 是这方面较好的数据库之一。您还可以通过锁定用户权限来减少攻击面。

从架构上讲,公开一个允许通用查询并应用安全限制的接口非常有意义。在某种程度上,随着 REST API 获得自定义查询等功能,人们正在重新发明轮子。

能否做到这一点,很大程度上取决于与用户的关系。如果这些是具有合同关系的付费客户,因此在某种程度上比随机互联网用户更受信任,那么使用这种架构可能是合适的。特别是如果不同的客户端被隔离在不同的数据库上。小心翼翼地行走。这种情况下,如果您遭受破坏,您可能会因此受到批评,尽管您已经仔细考虑了风险和收益。如果您正在运行具有匿名注册的网络规模服务,我会避免这种情况。尽管值得注意的是,大多数云平台提供商确实提供了向客户端公开数据库端口的功能。

我已经构建了两个 RESTful 接口,并为客户提供了直接的 SQL 访问。

这里的问题是这个问题从根本上是有缺陷的:

后端开发人员所做的许多工作是通过 HTTP 向客户提供 CRUD 访问,本质上是将数据从内部数据库映射到内部数据库。

根据我的经验,这不是我所做工作的重要部分。让我们将其简化为 4 个任务 RE 数据访问:

  1. 验证传入数据。
  2. 身份验证、授权和日志记录。
  3. 公开一组有限的功能。
  4. 提供对用户敏感的 API。

DB 通常不提供这些任务所需的工具来执行此操作。例如,我可能想要:

  1. 使用外部服务验证传入数据。
  2. 使用 OAuth 提供身份验证,使用角色提供对特定行的访问。然后,我有我想根据角色/数据访问编写的特定日志。
  3. 我可能只想公开某些报告(例如,出于性能(考虑 DoS)或业务原因)。
  4. SQL 不是我的大多数客户大部分时间想要的格式。

虽然我确信对于这些场景中的每一个都有一些具有某些功能的数据库......通常大多数数据库不会支持这些场景中的大多数,因为它们是数据库,因此不是为处理业务逻辑而设计的。

话虽如此,在某些情况下,客户需要数据库级别的访问——在这种情况下,您找到提供直接访问的解决方案。没有什么可以阻止这种情况发生 - 这并不典型。

表现

您说性能应该“更好”,只是现在您刚刚授予恶意行为者完全的权限来破坏数据库的性能。当然,他们必须进行身份验证,但“恶意”参与者也可能是“天真、无能”的合法用户。当用户开始在他们能找到的所有表上运行外连接时,你会怎么做?在你的数据库中每个非索引字段上都有 where 子句,以及计算量很大的计算字段?除非您的数据库非常小,否则您将面临这种风险。

我的猜测是您的数据库非常小,因为面向数据库的 webapp 应该做的一件大事就是缓存最常见的结果并非每个服务都可以做到这一点,因为有些服务被明确设计为提供完全一致的读/写访问。但是许多是只读的,并且可以容忍一些延迟 wrt 更新一致性。如果这些服务使用内存缓存(如 mecached、redis 等),它们实际上可以比直接 DB 访问快数千倍。

验证

除非您在需要某种业务规则验证的每个表上都有更新触发器,否则直接访问是破坏您的完整性的好方法。哦,那是某人刚刚写了字母字符的邮政编码字段?没问题。电话号码字段包含字母?美好的。货币字段包含逗号和分号?也许有人试图通过一些逻辑黑客来给自己免费奖金。您真的相信每个用户都能执行与您的 web 应用程序相同级别的验证吗?你应该放弃编码并成为一名牧师,因为你的信仰水平令人难以置信。

维护

有时您需要让您的数据库离线进行重大维护。发生这种情况时,缓存 web 应用程序至少可以继续为读取服务,但直接访问会影响整个用户社区。有时您希望将数据库迁移到更强大的服务器。那是什么?您无法让所有用户同时切换他们的连接字符串?有时您想切换到集群。哦,那些硬编码的连接字符串现在真的在@$$ 中咬你,不是吗?安全性是否只是因为他们更新了防火墙规则而要求您切换端口?嗯...是时候通知所有客户他们的连接字符串需要更新了。

结论

如果您打算永远不会拥有超过少数客户或超过几千行,并且您确信您的数据库/应用程序永远不会超出这个玩具大小,那么直接访问就是 Just Fine(TM)。另一方面,如果您的数据库有一天可能超出其当前的化身,或者您想要进行涉及重组、重新缩放或重新归位的重大迁移,那么您将感谢您的幸运之星,您可以保存该层间接性您的培根,并带来可扩展的高性能解决方案的所有优点。