問題描述
我有一個需要存儲服務(wù)器登錄信息的 Web 應(yīng)用程序.我使用 2048 位 PGP 公鑰來加密插入的密碼(請參閱 insertServerDef
)和帶有密碼的私鑰來解密密碼(請參閱 getServerDef
).>
據(jù)我所知,這條鏈中最薄弱的環(huán)節(jié)是私鑰和密碼的處理.正如您從我下面的代碼中看到的,我只是使用 file_get_contents
從位于當(dāng)前 Web 目錄中的文件中檢索密鑰和密碼——不好.
我的問題是:安全檢索用于解密登錄信息的私鑰和密碼的好方法是什么?也許我應(yīng)該通過經(jīng)過身份驗(yàn)證的遠(yuǎn)程文件服務(wù)器存儲/檢索私鑰?
我搜索了最佳實(shí)踐,但找不到太多.
class DB {受保護(hù)的 $_config;受保護(hù)的 $_iUserId;受保護(hù)的 $_iServerId;受保護(hù)的 $_dbConn;受保護(hù)的 $_sPubKey;受保護(hù)的 $_sPrivKey;公共函數(shù) __construct($iUserId, $iServerId) {//將全局配置數(shù)組引入本地范圍全局 $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"));}//連接數(shù)據(jù)庫公共函數(shù)連接(){嘗試 {$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 連接對象已創(chuàng)建";} catch(PDOException $e) {echo $e->getMessage();}}公共函數(shù) 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();}公共函數(shù) 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>";}}//關(guān)閉任何現(xiàn)有的數(shù)據(jù)庫連接公共函數(shù)關(guān)閉(){$this->_dbConn = null;}//在卸載時關(guān)閉任何現(xiàn)有的數(shù)據(jù)庫連接公共函數(shù) __destruct() {$this->_dbConn = null;}}
(注意:我不是安全專家.我對該領(lǐng)域感興趣,但僅此而已.記住這一點(diǎn).)
如果可能,根本不要存儲密碼
這在很大程度上取決于您的需求.最好的選擇是根本不使用雙向加密;如果您只能存儲 salted 和 單向散列 密碼摘要是理想的.您仍然可以測試它們以查看它們是否與用戶提供的密碼匹配,但您永遠(yuǎn)不會存儲它.
更好的是,如果您的客戶使用一些合理的協(xié)議(即:不是通常實(shí)施的 HTTP),您可以使用 挑戰(zhàn)-響應(yīng)身份驗(yàn)證機(jī)制,這意味著您的應(yīng)用永遠(yuǎn)永遠(yuǎn)需要查看用戶的密碼,即使在對其進(jìn)行身份驗(yàn)證時也不需要.遺憾的是,這在公共網(wǎng)絡(luò)上幾乎不可能,因?yàn)樗陌踩詴?80 年代的程序員感到羞恥.
如果您必須存儲密碼,請將密鑰與應(yīng)用程序隔離
如果您必須能夠解密密碼,理想情況下您不應(yīng)將所有詳細(xì)信息都放在一個地方,當(dāng)然也不是一個可復(fù)制、易于訪問的地方.
出于這個原因,我個人不希望為此目的使用 PgCrypto(正如您正在做的那樣),因?yàn)樗鼤仁鼓蚍?wù)器顯示私鑰和(如果有)密碼,它可能在那里暴露在 PostgreSQL 的日志文件中或以其他方式可能被嗅探.我想做我的加密客戶端,在那里我可以使用 PKCS#11、密鑰代理或其他工具來解密數(shù)據(jù),而無需我的代碼能夠訪問密鑰.
安全密鑰存儲問題是 PKCS#11 被發(fā)明的一部分.它為應(yīng)用程序和加密提供商提供了一個通用接口,可以與任何可以提供某些簽名和解密服務(wù)的東西進(jìn)行對話,無需透露其密鑰.通常(但不僅限于)使用基于硬件的加密,如智能卡和硬件加密模塊.此類設(shè)備可以被告知對傳遞給它們的數(shù)據(jù)進(jìn)行簽名或解密,并且可以在不泄露密鑰的情況下執(zhí)行此操作.如果可能,請考慮使用智能卡或 HSM.據(jù)我所知,PgCrypto 不能使用 PKCS#11 或其他 HSM/智能卡.
如果您不能這樣做,您仍然可以使用密鑰管理代理,在服務(wù)器啟動時您將密鑰手動加載到密鑰管理程序中,并且密鑰管理程序提供一個 PKCS#11(或某些其他)通過套接字進(jìn)行簽名和解密的接口.這樣您的 Web 應(yīng)用程序就根本不需要知道密鑰.gpg-agent
可能符合此目的.同樣,據(jù)我所知,PgCrypto 不能使用密鑰管理代理,盡管添加它會是一個很棒的功能.
即使是很小的改進(jìn)也會有所幫助.最好不要將您的密鑰的密碼短語存儲在磁盤上,因此您可能需要在應(yīng)用程序啟動時輸入它,以便可以解密密鑰.您仍然將解密的密鑰存儲在內(nèi)存中,但解密它的所有細(xì)節(jié)都不再存在于磁盤上,而且很容易獲得.攻擊者從內(nèi)存中竊取解密的密鑰比從磁盤中獲取password.txt"要困難得多.
您選擇做什么在很大程度上取決于您的安全需求的詳細(xì)信息以及您使用的數(shù)據(jù).在你的位置上,如果可能的話,我只是不存儲密碼,如果必須的話,我想使用與 PKCS#11 兼容的硬件設(shè)備.
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.
這篇關(guān)于存儲/檢索 PGP 私鑰和密碼的安全方法?的文章就介紹到這了,希望我們推薦的答案對大家有所幫助,也希望大家多多支持html5模板網(wǎng)!