Безопасный дизайн для кэширования многопользовательского соединения mod-perl2 SET AUTHORIZATION
У меня есть веб-приложение mod_perl2.0.4 / Apache2.2, работающее на CentOS 6.4 с PostgreSQL 9.0.
До недавнего времени у меня была такая настройка: Apache::DBI и DBI->connect_cached для всех соединений, которые начинал давать FATAL: sorry, too many clients already
даже в моей области разработки, где я единственный пользователь.
Чтобы отладить это, я удалил все ссылки на Apache::DBI, обновил до последней версии DBI и заменил все вхождения connect_cached на обычный DBI->connect. Теперь мне кажется, что сделано несколько меньше соединений, а затем оставлено <IDLE>
, Тем не менее, я понимаю, что не вызывал метод connect() для всех моих дескрипторов операторов, потому что это звучало так, как в 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 мог решить, следует ли отключаться?
Другими словами, без моей частной переменной соединения сохраняются ли эффекты SET SESSION AUTHORIZATION, когда следующий Apache::DBI->connect() повторно использует существующее соединение? Если да, возможно ли когда-либо, чтобы соединение повторно использовалось другим запросом, в то время как один запрос в настоящее время выполняется, но в настоящее время не использует дескриптор базы данных?
2 ответа
Это кажется безопасным. Чтобы "проверить", вы можете сделать искусственное состояние гонки следующим образом:
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";
}
... а затем добавьте два разных URL-адреса '? u =...'. Auth всегда будет соответствовать исходному auth, потому что dbh недоступен для передачи, пока он находится в скрипте, который выполняется.
Я рекомендую несколько другую тактику, если можете.
Сохраняйте это простым в Apache. Используйте индивидуальные сеансы для каждого пользователя, если это проще всего сделать безопасным и надежным.
Затем поместите PgBouncer между сервером PostgreSQL и вашим экземпляром Apache. Установите его в режим пула транзакций. Он с удовольствием мультиплексирует ваши соединения и позаботится о вызове DISCARD ALL при каждом переключении соединения между пользователями.
Я думаю, что вы все еще можете использовать SET SESSION AUTHORIZATION для соединений, сделанных через PgBouncer.