据了解,这条链中最薄弱的环节就是处理私钥和密码.从我下面的代码可以看出,我只是使用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; } }
如果可能的话,根本不要存储密码
这取决于你的需求.所有的最好的选择是不要使用双向加密;如果你只能存储这个理想的salted和one-way-hashed密码摘要.您仍然可以测试它们,以查看它们是否与用户提供的密码相匹配,但您不会存储它.
更好的是,如果您的客户使用一些稳定的协议(即:不是通常实现的HTTP),您可以使用challenge-response authentication mechanism,这意味着您的应用程序从来没有需要看到用户的密码,甚至不认证它们.可悲的是,这在公共网站上很少有可能,这有可能使80年代的程序员感到羞耻.
如果您必须存储密码,请将该密钥与该应用隔离开
如果您必须能够解密密码,那么理想情况下,您不应该在一个地方完成所有的细节,而且肯定不是一个可复制的,易于访问的地方.
因为这个原因,我个人不喜欢不要使用PgCrypto(正如你所做),因为它迫使你暴露私钥,(如果有的话)将密码泄露给服务器,在Postgresql的日志文件或以其他方式潜在嗅探.我想要做我的加密客户端,在那里我可以使用PKCS#11,一个关键代理或其他工具,让我解密数据,而不用让我的代码能够访问密钥.
安全密钥存储的问题是PKCS#11发明的一部分.它为应用程序和加密提供程序提供了一个通用接口,可以提供任何可以提供某些签名和解密服务的东西,而不会泄露其密钥.通常但不仅仅是使用基于硬件的加密,如智能卡和硬件加密模块.这样的设备可以被告知签署或解密传递给他们的数据,并且可以这样做,而不会泄露密钥.如果可能,请考虑使用智能卡或HSM.据我所知,PgCrypto不能使用PKCS#11或其他HSM /智能卡.
如果您不能这样做,您仍然可以使用密钥管理代理,在服务器引导时将密钥加载到密钥管理程序中,密钥管理程序提供PKCS#11(或其他一些)接口用于通过套接字进行签名和解密.这样你的网络应用程序根本就不需要知道密钥. gpg代理可能符合此目的.再次,据我所知,PgCrypto不能使用密钥管理代理,虽然这将是一个很好的功能添加.
即使有小的改进也可以帮助.如果您的密钥的密码不存储在磁盘上,那么最好是在启动应用程序时可能需要输入密码,以便密钥可以被解密.您仍然将解密的密钥存储在内存中,但解密的所有细节不再在磁盘上,而且易于获取.攻击者从内存中窃取解密的密钥比从磁盘获取“password.txt”要困难得多.
您所选择的做法取决于您的安全需求和您正在使用的数据的详细信息.在你的位置,我只是不存储密码,如果可能的话,如果我不得不使用PKCS#11兼容的硬件设备.