У меня есть веб-приложение, которое должно хранить информацию для входа в сервер. Я использую 2048-битный открытый ключ PGP для шифрования вставленных паролей (см. insertServerDef
) и закрытый ключ с парольной фразой для расшифровки паролей (см. getServerDef
).
Насколько я понимаю, самым слабым звеном в этой цепочке является обработка секретного ключа и кодовой фразы. Как вы можете видеть из моего кода ниже, я просто использую file_get_contents
для извлечения ключа и кодовой фразы из файлов, находящихся в текущем веб-каталоге, – это нехорошо.
Мой вопрос: что такое хороший метод безопасного получения секретного ключа и кодовой фразы для использования в расшифровке информации для входа? Может быть, я должен хранить / извлекать закрытый ключ через аутентифицированный удаленный файловый сервер?
Я искал лучшие практики, но не смог найти много.
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 / smartcards.
Если вы не можете этого сделать, вы все равно можете использовать агент управления ключами, где вы загружаете свой ключ в программу управления ключами вручную, когда сервер загружается, а программа управления ключами предоставляет интерфейс PKCS # 11 (или какой-либо другой) для подписи и дешифрования через сокет. Таким образом, ваше веб-приложение никогда не должно знать ключ вообще. gpg-agent
может претендовать на эту цель. Опять же, насколько я знаю, PgCrypto не может использовать агент управления ключами, хотя это будет отличная возможность добавить.
Даже небольшое улучшение может помочь. Лучше, если кодовая фраза для вашего ключа не будет сохранена на диске, поэтому вы можете потребовать ее ввода, когда приложение будет запущено, чтобы ключ можно расшифровать. Вы все еще сохраняете дешифрованный ключ в памяти, но все детали для его расшифровки больше не находятся на диске и легко доступны. Для злоумышленника намного сложнее украсть дешифрованный ключ из памяти, чем захватить «password.txt» с диска.
То, что вы выбираете, зависит от деталей ваших потребностей в безопасности и данных, с которыми вы работаете. В вашей позиции я бы просто не сохранил пароли, если это вообще возможно, и если бы мне пришлось использовать аппаратное устройство, совместимое с PKCS # 11.
Я считаю, что не может быть ответа, если вы не описываете сценарий угрозы, которого вы хотите избежать.
Позвольте мне перефразировать вашу ситуацию: у вас должен быть простой текстовый пароль для доступа к удаленным системам через SSH. Цель состоит в том, чтобы защитить этот пароль, но при необходимости он доступен.
Не существует способа защитить пароль и использовать его в форме обычного текста. Всегда есть способ расшифровать его, и для этого нужен как механизм, так и секретный ключ.
Вы можете попробовать немного повторить это, например, снова защитить этот секрет, но в конце вам нужно будет сохранить окончательный пароль обычного текста в переменной и передать его в схему аутентификации.
Каков сценарий угроз, которого вы хотите избежать?
Видите ли, всегда есть способ причинить вред. Наибольший вред может быть нанесен, если вы регулярно не проверяете неправильную деятельность в ваших системах. Как контролировать все файлы журналов для атак. Вероятно, отправьте журналы на другой сервер syslog, чтобы злоумышленник не мог их изменить, если они находятся на одном сервере. Если вы сделаете все возможное, чтобы предотвратить проникновение злоумышленников в вашу систему, тогда необходимо безопасно хранить секретную кодовую фразу.
Может быть, идея хранить кодовую фразу в ОЗУ, например, на RAM-диске или внутри выделенной памяти. Таким образом, если сервер украден, он, скорее всего, будет отключен и забудет парольную фразу. Но после перезагрузки у вас должен быть способ восстановить кодовую фразу из пульта ДУ для продолжения работы. Но опять же: если вы не можете обнаружить, что атакующий находится в вашей системе, бессмысленно, является ли кодовая фраза внутри ОЗУ или на магнитном диске – ее можно прочитать.
Интересно, почему вы имеете дело с паролями в первую очередь. Если я использую SSH, я всегда пытаюсь использовать SSH-ключи для криптографической аутентификации. Это более безопасно на целевом сервере, потому что одна учетная запись (например, root) может иметь несколько ключей SSH, а если одна из них хранится на вашем сервере, она может быть удалена без вмешательства в другие ключи. Да, эти ключи SSH также могут быть защищены паролем.
Возможно, я что-то схожу с тобой. В настоящее время я хочу защитить личную информацию в локальной базе данных веб-сервера, поэтому я шифрую ее открытым ключом (хранящимся на самом веб-сервере) и дешифруя его с помощью закрытого ключа, хранящегося в куки-файле с коротким сроком службы (30 минут для меня).
Благодаря SSL-соединению этот ключ не будет попадать в чужие руки, и он не будет хранить его на сервере. В идеале я должен дважды проверить, что PHP не кэширует значения cookie на сервере, но даже если это так, этот уровень безопасности по-прежнему представляет собой большую проблему для злоумышленников, чем просто кражу базы данных открытого текста.
Будет ли это хорошим подходом для вас, зависит от того, нужно ли вашему приложению получать доступ к учетным данным сервера, даже если пользователи не вошли в систему через Интернет. В моем случае дешифрование требуется только через веб-приложение, поэтому файл cookie достаточно. Однако, если вам требуется автоматическое использование, вам нужно будет сохранить закрытый ключ на сервере.