簡體   English   中英

存儲/檢索PGP私鑰和密碼的安全方法?

[英]Secure method for storing/retrieving a PGP private key and passphrase?

我有一個需要存儲服務器登錄信息的Web應用程序。 我正在使用2048位PGP公鑰來加密插入的密碼(請參閱insertServerDef )和帶密碼的私鑰來解密密碼(請參閱getServerDef )。

據我所知,這個鏈中最薄弱的環節是私鑰和密碼的處理。 從下面的代碼中可以看出,我只是使用file_get_contents從位於當前web目錄中的文件中檢索密鑰和密碼 - 不好。

我的問題是:安全檢索私鑰和密碼用於解密登錄信息的好方法是什么? 也許我應該通過經過身份驗證的遠程文件服務器存儲/檢索私鑰?

我搜索過最佳實踐,但卻找不到多少。

class DB {

    protected $_config;
    protected $_iUserId;
    protected $_iServerId;
    protected $_dbConn;
    protected $_sPubKey;
    protected $_sPrivKey;


    public function __construct($iUserId, $iServerId) {

        //bring the global config array into local scope
        global $config;
        $this->_config = $config;

        $this->_iUserId = $iUserId;
        $this->_iServerId = $iServerId;

        $this->_sPubKey = file_get_contents("public_key");
        $this->_sPrivKey = file_get_contents("private_key");
        $this->_sPrivKeyPass = trim(file_get_contents("private_key_pass"));

    }

    //connect to the database
    public function connect() {
        try {


            $this->_dbConn = new PDO("pgsql:host=".$this->_config['db_host']." dbname=".$this->_config['db_name'],$this->_config['db_username'],$this->_config['db_password']);

            echo "PDO connection object created";
        } catch(PDOException $e) {

            echo $e->getMessage();

        }

    }

    public function insertServerDef($sHost, $iPort, $sUser, $sPass) {

        //testing
        $iUserId = 1;

        $oStmt = $this->_dbConn->prepare("INSERT INTO upze_server_def (server_id, host_address, ssh_port, username, pass, user_id) VALUES (DEFAULT, :host_address, :ssh_port, :username, pgp_pub_encrypt(:pass,dearmor(:pub_key)), :user_id)");
        $oStmt->bindParam(':host_address',$sHost);
        $oStmt->bindParam(':ssh_port',$iPort);
        $oStmt->bindParam(':username',$sUser);
        $oStmt->bindParam(':pass',$sPass);
        $oStmt->bindParam(':pub_key',$this->_sPubKey);

        $oStmt->bindParam(':user_id',$iUserId);
        $oStmt->execute();

    }

    public function getServerDef($iServerId) {

        $oStmt = $this->_dbConn->prepare("  SELECT server_id, pgp_pub_decrypt(pass,dearmor(:priv_key),:priv_key_pass) As decryptpass 
                                            FROM upze_server_def usd 
                                            WHERE usd.server_id = :server_id
                                        ");

        $oStmt->bindParam(':server_id', $iServerId);
        $oStmt->bindParam(':priv_key', $this->_sPrivKey);
        $oStmt->bindParam(':priv_key_pass', $this->_sPrivKeyPass);
        $oStmt->execute();

        while($row = $oStmt->fetch()) {
            echo "<pre>".print_r($row)."</pre>";
        }

    }

    //close any existing db connection
    public function close() {
        $this->_dbConn = null;
    }


    //close any existing db connections on unload
    public function __destruct() {
        $this->_dbConn = null;
    }

}

(注意:我不是安全專家。我對該領域感興趣,但就是這樣。記住這一點。)

如果可能,請不要存儲密碼

這很大程度上取決於您的需求。 最好的選擇是根本不使用雙向加密; 如果你只能存儲理想的鹽漬單向散列密碼摘要。 您仍然可以測試它們以查看它們是否與用戶提供的密碼匹配,但您從不存儲它。

更妙的是,如果您的客戶端使用一些理智的協議(即:不為http普遍實施的),你可以使用一個挑戰-響應驗證機制從未意味着你的應用程序可能需要以看到用戶的密碼,進行身份驗證即使是在他們。 遺憾的是,在公共網站上很少有這種情況,因為它的安全性讓80位程序員感到羞恥。

如果必須存儲密碼,請從應用程序中隔離密鑰

如果您必須能夠解密密碼,理想情況下,您不應該在一個地方擁有所有細節,當然也不能在一個可復制的,易於訪問的地方。

出於這個原因,我個人不希望為此目的使用PgCrypto(正如你所做的那樣),因為它迫使你向服務器顯示私鑰和(如果有的話)密碼,它可以在PostgreSQL中暴露出來。日志文件或以其他方式可能被嗅探。 我想要我的加密客戶端,我可以使用PKCS#11,密鑰代理或其他工具,讓我解密數據,而無需我的代碼能夠訪問密鑰。

安全密鑰存儲的問題是PKCS#11發明的一部分。 它為應用程序和加密提供程序提供了一個通用接口,可以與任何可以提供某些簽名和解密服務的內容進行通信, 而無需泄露其密鑰 通常但不僅僅是使用基於硬件的加密,如智能卡和硬件加密模塊。 可以告訴這些設備簽署或解密傳遞給他們的數據,並且可以在不泄露密鑰的情況下這樣做。 如果可能,請考慮使用智能卡或HSM。 據我所知,PgCrypto不能使用PKCS#11或其他HSM /智能卡。

如果您不能這樣做,您仍然可以使用密鑰管理代理,在服務器引導時手動將密鑰加載到密鑰管理程序中,密鑰管理程序提供PKCS#11(或其他)接口用於通過套接字進行簽名和解密。 這樣你的網絡應用程序根本不需要知道密鑰。 gpg-agent可能有資格達到此目的。 同樣,據我所知,PgCrypto無法使用密鑰管理代理,盡管它是一個很棒的功能。

即使是小改進也可以提供幫助。 最好是密鑰的密碼不存儲在磁盤上,因此您可能需要在應用程序啟動時輸入密鑰才能解密密鑰。 您仍然將解密的密鑰存儲在內存中,但解密它的所有細節都不再存在於磁盤上且易於獲取。 攻擊者從內存中竊取解密密鑰比從磁盤中獲取“password.txt”要困難得多。

您選擇做什么在很大程度上取決於您的安全需求和您正在使用的數據的詳細信息。 在你的位置,如果可能的話,我不會存儲密碼,如果我不得不想要使用與PKCS#11兼容的硬件設備。

我可能正在做類似你的事情。 我目前希望保護本地Web服務器數據庫上的個人信息,因此我使用公鑰(存儲在Web服務器本身上)對其進行加密,並使用存儲在cookie中的私鑰對其進行解密,生命周期較短(30分鍾為我)。

通過SSL連接,這將使密鑰不會落入壞人之手,並且不會將密鑰存儲在服務器上。 理想情況下,我應該仔細檢查PHP不會在服務器上緩存cookie值,但即使這樣,這個安全層仍然是攻擊者的一個更大的障礙,而不僅僅是竊取明文數據庫。

這對您來說是否是一個好方法取決於您的應用程序是否需要訪問服務器憑據,即使用戶未通過Web登錄也是如此。 在我的情況下,只需通過Web應用程序進行解密,因此cookie就足夠了。 但是,如果需要無人參與,則需要將私鑰存儲在服務器上。

我相信除非您描述您想要避免的威脅情景,否則無法找到答案。

讓我重新說一下你的情況:你需要一個純文本密碼才能通過SSH訪問遠程系統。 目標是保護此密碼,但在需要時可以使用。

實際上沒有辦法保護密碼並且能夠以純文本形式使用。 總是有辦法解密它,這需要機制和密鑰。

您可以嘗試對此進行迭代,例如再次保護此秘密,但最終您需要將最終的純文本密碼存儲到變量中並將其傳遞給身份驗證方案。

您想避免哪種威脅方案?

  • 有人偷了你的數據庫轉儲,不應該知道存儲的密碼
  • 有人闖入您的服務器並使用秘密密碼短語讀取數據庫和文件
  • 有人闖入您的服務器,改變您的腳本並截取使用解密的純文本密碼的位置

你看,總有一種方法可以造成傷害。 如果您不定期檢查系統上的不規則活動,則可能會造成最大的傷害。 像監視所有日志文件以進行攻擊。 可能會將日志發送到另一個syslog服務器,以便攻擊者在同一服務器上無法更改它們。 如果您盡一切可能阻止攻擊者進入您的系統,那么安全存儲秘密密碼短語的需求就會減少。

將密碼短語存儲到RAM中可能是個主意,就像在RAM磁盤上或專用內存中一樣。 這樣,如果服務器被盜,它很可能會被取消,並忘記密碼短語。 但是,您必須有一種方法可以在重新啟動后從遠程恢復密碼短語繼續操作。 但同樣重要的是:如果您無法檢測到攻擊者在您的系統上,那么無論密碼短語是在RAM內還是在磁盤上都是無意義的 - 它可以被讀取。

我想知道你為什么要首先處理密碼。 如果我使用SSH,我總是嘗試使用SSH密鑰進行加密身份驗證。 這在目標服務器上更安全,因為一個帳戶(如root)可以有多個SSH密鑰,如果存儲在服務器上的密鑰被泄露,可以刪除它而不會干擾其他密鑰。 是的,這些SSH密鑰也可以用密碼保護。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM