安全地包装连接池

Posted

技术标签:

【中文标题】安全地包装连接池【英文标题】:Safely Wrapping a Connection Pool 【发布时间】:2017-02-12 20:19:02 【问题描述】:

我正在尝试实施行级安全性,以便我们的应用程序可以实施更严格的访问控制。

我们正在研究的其中一项技术是 Oracle 的虚拟专用数据库,它通过基本上使用 where 子句谓词增强针对特定表的所有查询来实现行级安全性。由于我们处于 Web 环境中,因此我们需要在 Oracle 中设置一个特殊的上下文,在单个请求的线程中。我们通过服务帐号使用连接池。

我开始研究 Eclipse Link 和 Hibernate。 Eclipse Link 似乎有 events 非常适合这个模型。

这将涉及我们从休眠状态迁移,这不是问题,但我们会为这些事件绑定到 EL。

Oracle 似乎暗示他们在Web Logic product 中的数据源级别实现。

上下文由 WebLogic 数据源代码设置和清除。

问题:在 DataSource 级别使用某些系列事件执行此操作是否更合适。我最应该关注的事件或方法是什么?

添加的问题:如何扩展连接池以使用一些自定义数据安全地初始化 oracle 上下文?我在 Apache 中进行挖掘,似乎扩展 BasicDataSource 并没有让我访问任何可以让我在 Spring 完成后清理连接的东西。

我需要建立一个连接,并清理一个连接作为退出/进入连接池。我希望有一个如此简单的实现,没有人可以通过打破产品的一些微妙平衡来搞砸它。

- Specifically we are currently using Apache Commons DBCP Basic Data Source

这将允许我们使用各种方式连接到数据库并仍然强制执行我们的安全性。但我没有看到一个很好的例子或一组事件可以使用,滚动我自己的安全生命周期从来都不是一个好主意。

【问题讨论】:

我不会在连接池方面这样做,因为连接池通常没有(我认为他们不应该有)关于用户执行请求或请求的信息。 oracle JDBC连接不应该最终维护oracle连接上下文吗? 我想是的。因此,如您所述,需要基于每个请求设置 Oracle VPD 参数,并在将连接返回到池时清除这些参数。对我来说,Eclipse Link 方法在这种情况下是最好的,但是我找不到 Hibernate/Hibernate+Spring 的等效方法,但我认为这是解决这个问题的最佳方法。 感谢您的帮助,我担心将我的安全模型绑定到高级框架。你同意吗?问题是,我可以扩展基本数据源,但我还必须扩展 GenericObject 池。我可以只覆盖 GetConnection,并始终在该方法中清除/设置信息,但是在将其返回池时我无法清除它。 多考虑一下,也许正如您所说,覆盖 DBCP BasicDataSource 是一个好方法。我会查看.getConnection()PoolableConnection.close() 进行清理,并检查使用汇集的准备好的语句是否会影响行为(尽管我不这么认为)。缓存肯定会影响行为,因此您可能也需要对此进行研究。我同意在 Hibernate 级别执行此操作并不理想。但是由于涉及池,您肯定需要至少按照您的描述更改您的连接池,我在这里发表评论。寻求进一步的建议。 【参考方案1】:

我最终通过扩展一些 Apache 组件解决了我的问题。

首先我扩展了org.apache.commons.pool.impl.GenericObjectPool 并覆盖了borrowObject()returnObject()。我知道池中对象的类型 (java.sql.Connection),因此我可以安全地投射和使用它们。

由于我使用的是 Oracle VPD,因此我能够在应用程序上下文中设置信息。我建议您更深入地阅读。这有点复杂,有很多不同的选项可以在各种上下文级别以及跨 RAC 节点隐藏或共享数据。 Start

本质上,我所做的是生成一个 nonce 并使用它在 oracle 中实例化一个会话,然后将用户的访问级别设置为该会话中的一个变量,然后 Oracle VPD 策略将读取并使用它来执行行级过滤。

我在覆盖的 borrowObject()returnObject() 中实例化并销毁了该信息我运行的 SQL 是这样的:

CallableStatement callStat =
                    conn.prepareCall("call namespace.cust_ctx_pkg.set_session_id(" + Math.random() + ")");
                  callStat.execute();

注意math.random() isn't a good nonce.

接下来是简单地扩展org.apache.commons.dbcp.BasicDataSource 并通过覆盖createConnectionPool() 来设置我的对象池。请注意,我这样做的方式禁用了一些我不需要的功能,因此您可能需要比我做的更多或更少的重写。

【讨论】:

【参考方案2】:

为了简单起见,您可以尝试任何对象级别的安全机制,例如 Spring Security ACL。

【讨论】:

谢谢,Spring ACL 运行后查询,这对我的需要来说太晚了。我需要强制执行保存数据的 ACL。但我的问题并不清楚,这对许多人来说是一个完全有效的解决方案。 您是否只想从用户拥有权限的数据库中获取那些记录?如果是,那么请问您是在谈论 dbUser 还是应用程序用户的权限? 这个想法是用 1 个 db 用户操作,但由客户端用户应用 ACL(许多未知品种) 我认为没有这样的现有解决方案(我也可能错了)。我建议使用 Spring ACL 为应用程序用户提供不同对象的权限(因为它易于使用)。然后编写自己的 sql 函数从 db 中获取数据,该函数将检查用户的权限(来自 ACL 表)并仅从表中返回所需的记录。 解决方案有很多种,例如 Oracle VPD【参考方案3】:

您将希望在应用程序层执行此操作。您将需要一个预提交钩子和一个后读钩子。

预提交挂钩用于确保来自客户端的数据由有权修改该数据的用户呈现。这可以防止未经授权的用户覆盖他们不应访问的数据。

这并不直观,但 post read 挂钩用于防止客户端访问不应允许用户查看的数据。这发生在查看后,因为这是在应用程序层而非数据层强制执行的。在从数据层检索数据之前,应用程序无法知道调用者是否被允许访问数据。在 post read 挂钩中,您根据登录用户的凭据评估返回的每一行的凭据,以确定是否允许访问。如果任何行的访问被拒绝,则会引发异常并且数据不会返回给客户端。

以这种方式实现的应用程序级安全性要求您能够将表中的每一行连接到访问它所需的权限/角色,并能够在运行时评估用户在服务器上的权限。

希望对您有所帮助。

【讨论】:

这并没有真正解决如何为 Oracle VPD 实例化上下文或清理它。不过,这当然是另一种行级安全性方法。 @AdmiralAdama 完整的问题以“我正在尝试实施行级安全性以便我们的应用程序可以实施更严格的访问控制”开头。陈述的问题是“在数据源级别通过一些事件来执行此操作是否更合适”。我的回复旨在建议这种类型的安全性通常与在应用程序层而不是在数据层管理的用户会话相关联,可以使用我建议的方法在应用程序层进行管理。 是的。我本来打算专注于如何利用 VPD。抱歉,如果我的问题不清楚。我对您的方法的问题是没有其他客户端可以安全地连接到数据。每个人都必须实施 ACL 策略。我们试图让它变得万无一失,所以没有人可以在不知道他们做了什么的情况下破坏它。【参考方案4】:

通过使用其他 Commons DBCP 数据源之一,您将获得更好的控制。 基本的就是这样:基本的:) org.apache.commons.dbcp.datasources 包中的那些给你更细粒度的控制。

【讨论】:

什么意思?在返回池阶段,我没有看到任何其他可以让我控制的方法?

以上是关于安全地包装连接池的主要内容,如果未能解决你的问题,请参考以下文章

golang 允许连接池的抽象包装器

数据库连接池的配置

postgresql pg.Pool 连接池凭据的安全性

面试官:了解数据库连接池吗?

MySQL与Redis数据库连接池介绍(图示+源码+代码演示)

数据库连接池的理解和使用