0

我有一个 mod_perl2.0.4 / Apache2.2 Web 应用程序在 CentOS 6.4 和 PostgreSQL 9.0 上运行。

直到最近,我才进行了这样的设置FATAL: sorry, too many clients already: Apache::DBI 和 DBI->connect_cached 用于所有连接,即使在我是唯一用户的开发区域中,它也开始提供。

为了调试它,我删除了对 Apache::DBI 的所有引用,升级到最新的 DBI,并将所有出现的 connect_cached 替换为普通的 DBI->connect。现在在我看来,建立的联系少了一些,然后就离开了<IDLE>。然而,我意识到我并没有在我的所有语句句柄上调用 disconnect(),因为它听起来像在 Apache::DBI 下它不会产生任何影响。

我的连接当前以同一个用户的身份连接,然后通过 SET SESSION AUTHORIZATION 降低他们的权限。我这样做是因为其他一些使用数据库的应用程序允许密码登录,这可以将凭据直接传递给数据库,但是这个特定的网络应用程序使用荣誉系统登录屏幕,您只需单击您的姓名即可登录。因此,它目前已为未来的安全做好准备,但也提供了便利。此外,历史等数据库触发器依赖于正确设置会话用户来跟踪谁做了什么。

因为我担心数据库句柄被错误的会话用户重用,所以我传递{ private_user_login => $login_role_name, PrintError => 0, RaiseError => 1, AutoCommit => 1}给 connect_cached 以按用户区分每个连接。但是由于我总是在连接后立即设置会话授权,所以我认为所有private_user_login哈希所做的都是使对于给定的 Apache 进程,如果最终创建和闲置的数据库连接数可能至少与用户数一样多每个用户都设法随机使用给定的 Apache 进程。同时,因为我没有断开任何手柄,它们最终会用完。

我的问题是,取出private_user_login使所有连接句柄看起来相同,减少打开的连接数量是否安全,或者是否有可能在中间重新使用连接句柄脚本(在设置会话用户之后)由不同的用户,从而创建一个竞争条件?此外,尽管 Apache::DBI 的文档说我不需要删除 disconnect()调用,但我是否应该在每个脚本的末尾都有这样的调用,以便 Apache::DBI 可以决定是否断开连接?

换句话说,如果没有我的私有连接变量,当下一个 Apache::DBI->connect() 重用现有连接时,SET SESSION AUTHORIZATION 的效果是否仍然存在?如果是这样,当一个请求当前正在执行但当前未使用数据库句柄时,是否有可能另一个请求重新使用连接?

4

2 回答 2

3

如果可以的话,我推荐一种不同的策略。

在 Apache 中保持简单。使用每个用户的私人会话,如果这是最容易确保安全和可靠的方法。

然后在 PostgreSQL 服务器和您的 Apache 实例之间放置一个 PgBouncer。将其设置为事务池模式。它会很高兴地多路复用您的连接,并且每当用户之间的连接切换时,它会负责调用 DISCARD ALL。

我认为您仍然可以在通过 PgBouncer 建立的连接上使用 SET SESSION AUTHORIZATION。

于 2013-12-13T03:42:11.383 回答
0

看起来很安全。要“验证”,您可以制作这样的人为竞争条件:

use Apache2::RequestUtil;
use Apache2::RequestRec;
my $r = Apache2::RequestUtil->request;
$r->headers_out->add('Cache-control' => "must-revalidate, no-cache, no-store");
require Apache2::Request;
my $req = Apache2::Request->new($r);
$r->content_type("text/html");
my $login_role_name = $req->param('u');
$r->print($u);
$r->print('<br>' . $$);
use DBI;

my $dbh = DBI->connect_cached("dbi:Pg:dbname=......,{ RaiseError => 1, AutoCommit => 1});
$dbh->do("set session authorization ?; ", undef, $login_role_name);
{
    use warnings NONFATAL => 'all';
    my $rows = $dbh->selectall_arrayref('select pg_backend_pid(), current_user::text');
    warn "pg ${$$rows[0]}[0] mp $$ auth: ${$$rows[0]}[1] original auth: $login_role_name";
    sleep 10;
    $rows = $dbh->selectall_arrayref('select pg_backend_pid(), current_user::text');
    warn "pg ${$$rows[0]}[0] mp $$ auth: ${$$rows[0]}[1] original auth: $login_role_name";
}

...然后用两个不同的 '?u=...' URLs 击中它。身份验证将始终与原始身份验证匹配,因为当 dbh 仍在执行的脚本中时,它不能被赠送。

于 2013-12-17T00:37:27.857 回答