問題描述
我有一個需要存儲服務器登錄信息的 Web 應用程序.我使用 2048 位 PGP 公鑰來加密插入的密碼(請參閱 insertServerDef
)和帶有密碼的私鑰來解密密碼(請參閱 getServerDef
).>
據我所知,這條鏈中最薄弱的環節是私鑰和密碼的處理.正如您從我下面的代碼中看到的,我只是使用 file_get_contents
從位于當前 Web 目錄中的文件中檢索密鑰和密碼——不好.
我的問題是:安全檢索用于解密登錄信息的私鑰和密碼的好方法是什么?也許我應該通過經過身份驗證的遠程文件服務器存儲/檢索私鑰?
我搜索了最佳實踐,但找不到太多.
class DB {受保護的 $_config;受保護的 $_iUserId;受保護的 $_iServerId;受保護的 $_dbConn;受保護的 $_sPubKey;受保護的 $_sPrivKey;公共函數 __construct($iUserId, $iServerId) {//將全局配置數組引入本地范圍全局 $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"));}//連接數據庫公共函數連接(){嘗試 {$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 連接對象已創建";} catch(PDOException $e) {echo $e->getMessage();}}公共函數 insertServerDef($sHost, $iPort, $sUser, $sPass) {//測試$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_earencrypt(:pass,d(: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();}公共函數 getServerDef($iServerId) {$oStmt = $this->_dbConn->prepare(" SELECT server_id, pgp_pub_decrypt(pass,dearmor(:priv_key),:priv_key_pass) 作為decryptpass從 upze_server_def 美元哪里 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>";}}//關閉任何現有的數據庫連接公共函數關閉(){$this->_dbConn = null;}//在卸載時關閉任何現有的數據庫連接公共函數 __destruct() {$this->_dbConn = null;}}
(注意:我不是安全專家.我對該領域感興趣,但僅此而已.記住這一點.)
如果可能,根本不要存儲密碼
這在很大程度上取決于您的需求.最好的選擇是根本不使用雙向加密;如果您只能存儲 salted 和 單向散列 密碼摘要是理想的.您仍然可以測試它們以查看它們是否與用戶提供的密碼匹配,但您永遠不會存儲它.
更好的是,如果您的客戶使用一些合理的協議(即:不是通常實施的 HTTP),您可以使用 挑戰-響應身份驗證機制,這意味著您的應用永遠永遠需要查看用戶的密碼,即使在對其進行身份驗證時也不需要.遺憾的是,這在公共網絡上幾乎不可能,因為它的安全性會讓 80 年代的程序員感到羞恥.
如果您必須存儲密碼,請將密鑰與應用程序隔離
如果您必須能夠解密密碼,理想情況下您不應將所有詳細信息都放在一個地方,當然也不是一個可復制、易于訪問的地方.
出于這個原因,我個人不希望為此目的使用 PgCrypto(正如您正在做的那樣),因為它會迫使您向服務器顯示私鑰和(如果有)密碼,它可能在那里暴露在 PostgreSQL 的日志文件中或以其他方式可能被嗅探.我想做我的加密客戶端,在那里我可以使用 PKCS#11、密鑰代理或其他工具來解密數據,而無需我的代碼能夠訪問密鑰.
安全密鑰存儲問題是 PKCS#11 被發明的一部分.它為應用程序和加密提供商提供了一個通用接口,可以與任何可以提供某些簽名和解密服務的東西進行對話,無需透露其密鑰.通常(但不僅限于)使用基于硬件的加密,如智能卡和硬件加密模塊.此類設備可以被告知對傳遞給它們的數據進行簽名或解密,并且可以在不泄露密鑰的情況下執行此操作.如果可能,請考慮使用智能卡或 HSM.據我所知,PgCrypto 不能使用 PKCS#11 或其他 HSM/智能卡.
如果您不能這樣做,您仍然可以使用密鑰管理代理,在服務器啟動時您將密鑰手動加載到密鑰管理程序中,并且密鑰管理程序提供一個 PKCS#11(或某些其他)通過套接字進行簽名和解密的接口.這樣您的 Web 應用程序就根本不需要知道密鑰.gpg-agent
可能符合此目的.同樣,據我所知,PgCrypto 不能使用密鑰管理代理,盡管添加它會是一個很棒的功能.
即使是很小的改進也會有所幫助.最好不要將您的密鑰的密碼短語存儲在磁盤上,因此您可能需要在應用程序啟動時輸入它,以便可以解密密鑰.您仍然將解密的密鑰存儲在內存中,但解密它的所有細節都不再存在于磁盤上,而且很容易獲得.攻擊者從內存中竊取解密的密鑰比從磁盤中獲取password.txt"要困難得多.
您選擇做什么在很大程度上取決于您的安全需求的詳細信息以及您使用的數據.在你的位置上,如果可能的話,我只是不存儲密碼,如果必須的話,我想使用與 PKCS#11 兼容的硬件設備.
I have a web application that needs to store server login information. I'm using a 2048bit PGP public key to encrypt inserted passwords (see the insertServerDef
) and a private key with a passphrase to decrypt the passwords (see getServerDef
).
As I understand things, the weakest link in this chain is the handling of the private key and passphrase. As you can see from my code below, I'm just using file_get_contents
to retrieve the key and passphrase from files located in the current web directory--not good.
My question is: what is a good method for securely retrieving the private key and passphrase for use in decrypting login info? Maybe I should store/retrieve the private key via an authenticated remote file server?
I've searched for best practices, but haven't been able to find much.
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;
}
}
(Note: I'm no security expert. I have an interest in the area, but that's it. Keep that in mind.)
If possible, don't store passwords at all
It depends a lot on what your needs are. The best option of all is not to use two-way encryption at all; if you can store only salted and one-way-hashed password digests that's ideal. You can still test them to see if they match a supplied password from the user, but you never store it.
Better still, if your clients use some sane protocol (ie: not HTTP as commonly implemented) you can use a challenge-response authentication mechanism that means your app never ever needs to see the user's password, not even when authenticating them. Sadly this is rarely possible on the public web, which has security that'd put 80's programmers to shame.
If you must store the password, isolate the keys from the app
If you must be able to decrypt the passwords, ideally you shouldn't have all the details to do so in one place, and certainly not one copyable, easily accessible place.
For that reason I'd personally prefer not to use PgCrypto (as you're doing) for this purpose because it forces you to reveal the private key and (if it has one) passphrase to the server, where it could be exposed in PostgreSQL's log files or otherwise potentially sniffed. I'd want to do my crypto client-side, where I could use PKCS#11, a key agent, or other tools that let me decrypt the data without ever having my code able to access the key.
The problem of secure key storage is part of what PKCS#11 was invented for. It provides a generic interface for applications and crypto providers to talk to anything that can provide certain signing and decryption services without ever revealing its key. The usual, but not only, use is with hardware based crypto like smart cards and hardware crypto modules. Such devices can be told to sign or decrypt data passed to them, and can do so without ever revealing the key. If possible, consider using a smartcard or HSM. As far as I know PgCrypto cannot use PKCS#11 or other HSMs/smartcards.
If you can't do that, you can still probably use a key management agent, where you load your key into a key management program manually when the server boots, and the key management program provides a PKCS#11 (or some other) interface for signing and decryption via a socket. That way your web app never needs to know the key at all. gpg-agent
may qualify for this purpose. Again, as far as I know PgCrypto cannot use a key management agent, though it'd be a great feature to add.
Even a small improvement can help. It's best if the passphrase for your key isn't stored on disk, so you might require it to be entered when the app is started up so the key can be decrypted. You're still storing the decrypted key in memory, but all the details to decrypt it are no longer on disk and easy to get at. It's much harder for an attacker to steal the decrypted key from memory than to grab a "password.txt" from disk.
What you choose to do depends a lot on the details of your security needs and the data you're working with. In your position I'd just not store the passwords if at all possible, and if I had to I'd want to use a PKCS#11-compatible hardware device.
這篇關于存儲/檢索 PGP 私鑰和密碼的安全方法?的文章就介紹到這了,希望我們推薦的答案對大家有所幫助,也希望大家多多支持html5模板網!