2013-12-12 17 views
0

我有一個mod_perl2.0.4/Apache2.2網絡應用程序運行在CentOS 6.4與PostgreSQL 9.0。SET SESSION AUTHORIZATION安全設計多用戶mod-perl2連接緩存

直到最近,我還有this setup:Apache :: DBI和DBI-> connect_cached用於所有連接,即使在我唯一的用戶開發區域,它也開始給予FATAL: sorry, too many clients already

爲了調試這個,我已經刪除了所有對Apache :: DBI的引用,升級到最新的DBI,並用普通的DBI-> connect替換了所有出現的connect_cached。在我看來,現在有點少的連接,然後離開<IDLE>。但是,我意識到我沒有在我的所有語句句柄上調用disconnect(),因爲它聽起來像在Apache :: DBI下,它沒有任何區別。

我的連接當前連接所有用戶爲同一用戶,然後通過SET SESSION AUTHORIZATION降低他們的權限。我這樣做是因爲其他一些使用數據庫的應用程序允許使用密碼登錄,它可以將憑證直接傳遞到數據庫,但是這個特定的Web應用程序使用榮譽系統登錄屏幕,只需點擊您的名字即可登錄。所以這是未來的安全準備,但目前是方便的。另外,數據庫觸發歷史記錄等依賴會話用戶正確設置以跟蹤誰做了什麼。

由於我擔心數據庫句柄會被錯誤的會話用戶重複使用,因此我將{ 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的效果會持續嗎?如果是這樣,有可能連接被另一個請求重新使用,而一個請求當前正在執行,但目前沒有使用數據庫句柄?

回答

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 = ...?」。 auth將始終與原始auth相匹配,因爲dbh在執行腳本時仍不可用。

3

如果可以的話,我推薦一個不同的方法。

在Apache中保持簡單。每個用戶使用私人會話,如果這是最簡單的安全和可靠。

然後在PostgreSQL服務器和Apache實例之間放置一個PgBouncer。將其設置爲事務池模式。它會愉快地複用您的連接,並且每當連接在用戶之間切換時,它都會負責調用DISCARD ALL。

我認爲你仍然可以在通過PgBouncer進行連接時使用SET SESSION AUTHORIZATION。

+0

謝謝,我考慮過這個,但我也讀過另一個SO線程,PgBouncer更多的是一個權宜之計。我試圖找到設計問題的根源,因爲我想我一定誤解了一些東西,以便首先解決這個問題。另外我更喜歡一個解決方案,它修復了我的代碼,而不是另一個應用程序依賴項。 – Kev

+0

雖然,這是否會解決Apache :: DBI文檔中描述的限制,「它將數據庫連接保持在每個進程的基礎上」?也就是說,PgBouncer會這樣做,以便通過兩個不同的Apache進程連接到數據庫的*相同用戶*最終將使用由PgBouncer管理的相同數據庫連接? (在這種情況下,我會考慮PgBouncer的初始設計而不是權宜之計,這是否意味着PgBouncer僅與PostgreSQL核心分離,因爲這適用於Web服務器環境,但不適用於桌面環境? – Kev

+1

@Kev不,它贏得了'噸,但基本上什麼都不會。如果你需要的話,你可能需要重新考慮你的應用程序,因爲幾乎沒有任何東西可以在你想要的地方做到。 –