13

サーバーのログイン情報を保存する必要のある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;
    }

}
4

3 に答える 3

8

(注: 私はセキュリティの専門家ではありません。この分野に興味はありますが、それだけです。覚えておいてください。)

可能であれば、パスワードをまったく保存しない

それはあなたのニーズが何であるかに大きく依存します. 最善の選択肢は、双方向暗号化をまったく使用しないことです。ソルト化された一方向ハッシュのパスワードダイジェストのみを保存できる場合、それは理想的です。ユーザーから提供されたパスワードと一致するかどうかをテストすることはできますが、保存することはありません。

さらに良いことに、クライアントが適切なプロトコル (つまり、一般的に実装されている HTTP ではないプロトコル) を使用している場合は、チャレンジ/レスポンス認証メカニズムを使用できます。これは、アプリがユーザーを認証するときでさえ、ユーザーのパスワードを見る必要がないことを意味します。悲しいことに、これは 80 年代のプログラマーを恥じさせるセキュリティを備えた公共の Web ではめったに不可能です。

パスワードを保存する必要がある場合は、キーをアプリから分離します

パスワードを解読できなければならない場合、理想的には、そのためのすべての詳細を 1 か所にまとめるべきではありません。

そのため、PgCrypto (あなたが行っているように) をこの目的で使用しないことを個人的に好みます。これは、秘密鍵と (ある場合) サーバーへのパスフレーズを公開する必要があるためです。ログファイルまたはその他の方法で傍受される可能性があります。PKCS#11、キーエージェント、またはコードがキーにアクセスできなくてもデータを復号化できるその他のツールを使用できる暗号化クライアント側を実行したいと思います。

安全なキー ストレージの問題は、PKCS#11が発明された目的の一部です。これは、アプリケーションと暗号化プロバイダーが特定の署名および復号化サービスを提供できるものと対話するための汎用インターフェイスを提供し、そのキーを公開することはありません。それだけではありませんが、通常は、スマート カードやハードウェア暗号モジュールなどのハードウェア ベースの暗号を使用します。このようなデバイスは、渡されたデータに署名または復号化するように指示することができ、キーを明らかにすることなく実行できます。可能であれば、スマートカードまたは HSM の使用を検討してください。私の知る限り、PgCrypto は PKCS#11 やその他の HSM/スマートカードを使用できません。

それができない場合でも、おそらくキー管理エージェントを使用できます。このエージェントでは、サーバーの起動時に手動でキーをキー管理プログラムにロードし、キー管理プログラムは PKCS#11 (またはその他の) インターフェイスを提供します。ソケットを介した署名と復号化。そうすれば、Web アプリはキーをまったく知る必要がありません。gpg-agentこの目的に適合する可能性があります。繰り返しますが、私が知る限り、PgCrypto はキー管理エージェントを使用できませんが、追加するのは素晴らしい機能です。

小さな改善でも役立ちます。キーのパスフレーズがディスクに保存されていないことが最善です。そのため、キーを復号化できるように、アプリの起動時にパスフレーズの入力が必要になる場合があります。復号化されたキーは引き続きメモリに保存されますが、復号化するためのすべての詳細はディスク上になく、簡単に取得できます。攻撃者にとって、ディスクから「password.txt」を取得するよりも、復号化されたキーをメモリから盗む方がはるかに困難です。

何を選択するかは、セキュリティ ニーズの詳細と使用しているデータに大きく依存します。あなたの立場では、私は可能な限りパスワードを保存したくありません。また、必要であれば、PKCS#11 互換のハードウェア デバイスを使用したいと思います。

于 2012-09-08T10:03:29.940 に答える
3

私はあなたと同じようなことをしているかもしれません。私は現在、ローカル Web サーバー データベース上の個人情報を保護したいと考えているため、公開鍵 (Web サーバー自体に格納されている) で暗号化し、有効期間が短い (30 分自分)。

SSL 接続を介して、これによりキーが悪意のある人の手に渡るのを防ぎ、サーバーにキーを保存しません。理想的には、PHP が Cookie の値をサーバーにキャッシュしていないことを再確認する必要がありますが、たとえキャッシュしていたとしても、このセキュリティ層は攻撃者にとってプレーンテキスト データベースを単純に盗むことよりも大きなハードルとなります。

これが適切なアプローチであるかどうかは、ユーザーが Web 経由でログインしていない場合でも、アプリケーションがサーバー資格情報にアクセスする必要があるかどうかによって異なります。私の場合、復号化は Web アプリ経由でのみ必要なので、Cookie で十分です。ただし、無人で使用する必要がある場合は、秘密鍵をサーバーに保存する必要があります。

于 2013-01-10T22:38:29.600 に答える
2

回避したい脅威のシナリオを説明しない限り、答えはないと思います。

あなたの状況を言い換えてみましょう: SSH 経由でリモート システムにアクセスするには、平文のパスワードが必要です。目標は、このパスワードを保護することですが、必要なときに利用できるようにします。

パスワードを保護し、プレーンテキスト形式で使用できるようにする方法は実際にはありません。復号化する方法が常に必要であり、これにはメカニズムと秘密鍵の両方が必要です。

このシークレットを再度保護するなど、これを少し繰り返すことができますが、最終的には、最終的な平文パスワードを変数に保存し、それを認証スキームに渡す必要があります。

回避したい脅威のシナリオは何ですか?

  • 誰かがあなたのデータベースのダンプを盗み、保存されたパスワードを知っているべきではありません
  • 誰かがあなたのサーバーに侵入し、秘密のパスフレーズでデータベースとファイルの両方を読み取ります
  • 誰かがサーバーに侵入し、スクリプトを変更して、復号化されたプレーン テキストのパスワードが使用されるポイントで傍受します。

ほら、害を及ぼす方法は常にあります。システムでの不規則なアクティビティを定期的にチェックしないと、ほとんどの損害が発生する可能性があります。同様に、攻撃のすべてのログ ファイルを監視します。おそらくログを別の syslog サーバーに送信して、同じサーバー上にある場合に攻撃者がログを変更できないようにします。攻撃者がシステムに侵入するのを防ぐためにできる限りのことをすれば、秘密のパスフレーズを安全に保管する必要性は減少します。

パスフレーズを RAM ディスクや専用メモリなどの RAM に保存することをお勧めします。このようにすると、サーバーが盗まれた場合、電源が切れてパスフレーズを忘れてしまう可能性が高くなります。ただし、再起動後に操作を続行するには、リモートからパスフレーズを復元する方法が必要です。しかし繰り返しになりますが、攻撃者がシステムにいることを検出できない場合、パスフレーズが RAM 内にあるか磁気ディスク上にあるかは意味がありません。読み取り可能です。

そもそもなぜパスワードを扱うのか不思議です。SSH を使用する場合は、暗号化認証に常に SSH キーを使用しようとします。1 つのアカウント (root など) が複数の SSH キーを持つことができ、サーバーに保存されているキーが侵害された場合、他のキーに干渉することなく削除できるため、これはターゲット サーバーでより安全です。はい、これらの SSH キーもパスワードで保護できます。

于 2013-01-09T21:58:35.130 に答える