Java で安全な静的ログイン資格情報システムを実装するにはどうすればよいでしょうか?
-
01-07-2019 - |
質問
最近セキュリティ監査を実施したところ、ここで導入されているシステムのいくつかの弱点が明らかになりました。その結果として生じるタスクの 1 つは、パートナー認証情報システムを更新して安全性を高める必要があることです。
「古い」やり方では、(不適切な) パスワードを生成し、それを ID とともにパートナーに渡し、パートナーはその ID とそのパスワードの Base 64 でエンコードされたコピーを、https 経由のすべての XML リクエストとともに送信することでした。 。次に、それらをデコードして検証します。
これらのパスワードは変更されません (その場合、パートナーはパスワードを変更するためにコーディングや構成の変更を行う必要があり、複数の環境で何百ものパートナーとパスワードの有効期限を調整するのは悪夢のような作業になるため)、人間がパスワードを入力する必要はありません。または人間が読める形式。パートナー向けに、より良い、しかし比較的シンプルな実装があれば、これを変更することに前向きです。
基本的には次の 2 つのことに帰着します。より安全な Java パスワード生成システムと、パスワードが安全な方法で送信されることを保証する必要があります。
手動で作成したパスワード生成ツールをいくつか見つけましたが、これを行うための標準的な方法として特に目立ったものはありませんでした (おそらく正当な理由があるのでしょう)。https を介した単純な Base 64 エンコードよりも安全な送信方法がある可能性もあります。
パスワード生成者はどうしますか?また、その送信方法は十分に安全だと思いますか?
編集:XML は SOAP メッセージに含まれており、資格情報は XML 自体ではなくヘッダーに含まれています。また、パスワードを設定するときは各パートナーに対して 1 回限りの操作であるため、ジェネレーターの効率についてはあまり心配しません。
解決
パスワードの生成
送信用のパスワードのエンコードに関して、本当にセキュリティを強化できる唯一のエンコードは暗号化です。Base-64 または 16 進数を使用するのはセキュリティのためではなく、XML などのテキスト形式に含めることができるようにするためです。
エントロピーはパスワードの品質を測定するために使用されます。したがって、ランダムな「コイン投げ」で各ビットを選択すると、最高品質のパスワードが得られます。パスワードは他の暗号キーと同じくらい強力なものにする必要があるため、少なくとも 128 ビットのエントロピーをお勧めします。
パスワードをテキストとしてエンコードする方法に応じて、2 つの簡単な方法があります (セキュリティの観点からはまったく問題ありません)。
のために Base-64, 、次のようなものを使用します。
SecureRandom rnd = new SecureRandom();
/* Byte array length is multiple of LCM(log2(64), 8) / 8 = 3. */
byte[] password = new byte[18];
rnd.nextBytes(password);
String encoded = Base64.encode(password);
以下では、Base-64 エンコーダを考え出す必要はありません。結果として得られるエンコードはそれほどコンパクトではなく (24 文字ではなく 26 文字)、パスワードのエントロピーはそれほど多くありません。(しかし、130 ビットという数字はすでにかなりの量であり、人間が選択した少なくとも 30 文字のパスワードに匹敵します。)
SecureRandom rnd = new SecureRandom();
/* Bit length is multiple of log2(32) = 5. */
String encoded = new BigInteger(130, rnd).toString(32);
新しい SecureRandom オブジェクトの作成には計算コストがかかるため、パスワードを頻繁に生成する場合は、インスタンスを 1 つ作成して保持しておくとよいでしょう。
より良いアプローチ
XML にパスワードを埋め込むこと自体が間違いのように思えます。
まず、送信者から送信された文書を処理する前に、送信者を認証する必要があると思われます。私があなたの根性が嫌いで、サービス拒否攻撃を実行するために巨大な XML ファイルを送信し始めたとします。XML を解析するだけで、私が正規のパートナーではないことがわかりますか?サーブレットが認証されていないユーザーからのリクエストを前もって拒否したほうが良いのではないか?
第 2 に、正規のパートナーのパスワードは HTTPS による送信中に保護されていましたが、現在はシステムのどこかに「平文で」保存されている可能性があります。それはセキュリティが悪いです。
より良いアプローチは、パートナーが HTTP リクエスト ヘッダーに資格情報を含むドキュメントを送信するときにパートナーを認証することです。HTTPS のみを許可する場合は、ドキュメントからパスワードを完全に取り出して、 HTTP「基本」認証 代わりにヘッダー。これは送信中に SSL によって保護され、平文でシステムに保存されません (一方向ハッシュは認証目的でのみ保存されます)。
HTTP 基本認証はシンプルで広くサポートされており、SSL クライアント証明書よりも実装がはるかに簡単です。
文書コンテンツの保護
文書自体の内容が機密である場合は、送信者が文書を暗号化し、暗号化された形式で保存する必要があります。これを行う最良の方法は公開キー暗号化を使用することですが、それについては別の質問で取り上げます。
他のヒント
認証にSSLキーは使えないのでしょうか?
SSL 経由 (HTTPS 経由) でパスワードを送信することが、なぜ監査チームによって「安全ではない」とみなされるのかは不明です。したがって、2 つのことを要求すると、2 番目のパスワードが安全な方法で送信されるようにすることは、すでに適切に処理されているようです。
1 つ目に関しては、監査でパスワードが安全ではないことが明らかになったということを知る必要があります...
私なら、パスワードによるアプローチ全体を放棄し、双方向認証された SSL 接続を許可するクライアント証明書の使用を開始します。
クライアントごとに個別の証明書を生成して署名する必要があります。SSL ハンドシェイクでは、クライアントの証明書を要求し、それを検証します。失敗すると、接続は 401 ステータス コードで終了します。
証明書はいつでも取り消すことができるため、元の顧客との接続を簡単に切断できます。
これらすべては通信前のハンドシェイクで行われるため、サーバーにデータが大量に送信されることはありません。