web-dev-qa-db-ja.com

秘密鍵をファイルに保存する必要がありますか?

Ssh経由で別のサーバーと通信する必要があるdb駆動型アプリケーションがあります。 Webアプリは、キーペアを生成してユーザーに公開鍵を提供するか、Webフォームのフィールドで暗号化されていない秘密鍵を受け入れることができます。次に、キーを可逆暗号化で暗号化し、データベースに保存します。

Webアプリがsshサーバーと通信するときは、秘密鍵の暗号化を解除して、何らかの方法でsshに提供する必要があります。 mktempを使用して安全な一時ファイルを作成し、sshをポイントしてファイルを破棄することもできますが、それは必要ではないように思われる新しい方法でキーを公開する余分な作業です。 man sshman ssh-agentman ssh-addを見てきましたが、最初にファイルに追加せずにキーを使用する方法はないようです。何が足りないのですか?

7
kojiro

ssh/_ssh-agent_のような既存のツールを使用する場合は、キーをファイルとして提供する必要があります。

もう1つの、おそらくより実行可能な解決策は、アプリケーション内にsshクライアントを直接実装し、 JSch などのサードパーティライブラリに依存するか、ssh/_ssh-agent_データベースからキーを直接受信して復号化します。

4
Ulrich Dangel

ファイルは、アプリケーション間でデータを交換するための主な方法です。ディスクファイルである必要はありません。一時ストレージ上のファイルまたはパイプの場合があります。キーファイルがパスワードで保護されていない限り、標準入力でssh-addにキーファイルをフィードできます¹(キーファイルは暗号化されて保存されているため、でパスワードで保護される理由はありません)上)。

または、特別なことをせずに、ファイルを/run(または/dev/shmまたは/tmp、またはディストリビューションが提供するもの)などのメモリベースのファイルシステムに書き込みます。

または、秘密鍵ファイルを外部暗号化なしで保持し、パスワードを設定します。ランダムに生成された長いパスソードを使用し、そのパスワードをデータベースに保存します。

¹ 実験的に、ssh-addは、名前付きパイプなど、シークできないパスワードで保護されたキーファイルをチョークします。

申し訳ありませんが、回答ではありませんが、コメントするには長すぎます。

重要なことは、暗号化されたデータベースにキーを保存することによって達成しようとしていることです。

一時的な秘密鍵ファイルにアクセスできる攻撃者は、sshクライアントプロセスのメモリも読み取ることができます(したがって、とにかく鍵データにアクセスできます)。これらのデータは両方とも基本的に同じアクセス許可を持っている必要があるためです。ファイルを非表示にすることは、それ以上安全にならないようです。

Db、webapp、およびssh接続に異なるユーザーを使用している場合、キーをdbに格納し、webappで復号化し、sshにフィードすることで、プロセス全体にキーを拡散することで潜在的な攻撃ベクトルを開くだけです。これらすべて(db、app、ssh)に1人のユーザーのみを使用している場合、コードの複雑さ以外は何も得られません。

唯一の利点は、システムを別のホストに簡単に転送できることと、dbデータが盗まれたが、webapp(AFAIUには復号化アルゴリズムとパスワードが含まれている)が盗まれた場合の潜在的な利益であるようです。しかし、それはありそうですか?

とはいえ、キーを保護したい場合は、sshのキーの内部暗号化を使用して、サービスの開始時にssh-agentにロードすることもできます(停止したら削除することを忘れないでください)。ただし、ssh-agentはメモリ内のキーを暗号化せずに保持するため、読み取ることができる可能性があることに注意してください。もう一度:thisはあなたが解決しようとしている問題ですか?

2台のマシン間の通信を保護する必要があるだけの場合は、 stunnel の方がsshよりも優れている可能性があります。

そして最後に質問があります: OpenSSHportable )に関する限り、sshが別のプロセスからキーを読み取れるようにする簡単なパッチを作成しますsshdAuthorizedKeysCommandオプションで行うように、かなり簡単なはずです。

3
peterph

キーペアを生成し、Pythonを使用してデータベースに保存しているときに、この問題を解決しようとしていました。以下は、私が使用することになったいくつかのステップ、つまりssh-agentを開始してそれと対話することです。 Pythonですが、おそらく他の何かに翻訳することができます。もう1つの機能は、これらのいずれもShell=Trueを使用しないため、インジェクション攻撃からより安全であるということです。

import subprocess
import os
sockfile = '/some/place.sock'

agent = subprocess.Popen(['ssh-agent',
          '-D', # foreground mode
          '-a', sockfile, # bind address (socket file)
     ])
subprocess.check_call(['ssh-add', 
           '-D', # delete all identities from the agent
      ], 
      env={'SSH_AUTH_SOCK': sockfile},
)

これにより、プログラム専用のssh-agentが起動し、クリアされます(ユーザーキーがロードされていると思いますが、確かではありません)。環境変数SSH_AUTH_SOCKは通常ssh-agentによって設定されます(ただし、手動で行いました)。他のプログラムへの給餌。

次に、ssh-addを使用して秘密鍵をフィードします(ここでは、dr.private_keyに文字列として格納されていますが、シリアル化する必要があるため、ASCIIとしてエンコードします)。

adder = subprocess.Popen(['ssh-add', 
        '-', # read key from stdin
     ], 
     env={'SSH_AUTH_SOCK': sockfile}, 
     stdin=subprocess.PIPE,
)
adder.communicate(dr.private_key.encode('ascii'))
retval = adder.wait()
assert retval == 0 # added successfully

これで、SSHを使用するアプリケーションを使用するときはいつでも、たとえばGit、同じSSHエージェントソケットを使用するように参照します。

gitp = subprocess.Popen(
        ['git', '-C', os.path.abspath('../some-repo') ,'fetch'], 
        env={'SSH_AUTH_SOCK': sockfile}, 
        stdout=subprocess.PIPE, 
        stderr=subprocess.STDOUT,
    )
print(gitp.communicate()[0].decode('ascii'))

そしてそれはうまく機能します。唯一の「一時ファイル」はssh-agentによって使用されるソケットですが、所有者のみがアクセス許可を持つように作成されており、エージェントを終了すると削除されます。

2
Nick T