プライベートSSHキーのセキュリティを強化します

sshキーの仕組みに興味がありますか? それとも、どれだけ安全なのでしょうか?



私は毎日何度もsshを使用していますgit fetch



またはgit push



を実行するとき、コードをデプロイするとき、またはサーバーにログインするとき。 少し前まで、私はsshがその仕事の原理を理解することなく使用していた魔法になったことに気付きました。 私はそれがあまり好きではありませんでした-私は使用するツールを理解するのが好きです。 それで、私は少し研究をして、あなたと結果を共有しました。



プレゼンテーションの過程で、多くの略語があります。 アイデアを理解するのに役立ちませんが、詳細をグーグルで検索することにした場合に役立ちます。



そのため、キーによる認証に頼らなければならない場合、ホームディレクトリに~/.ssh/id_rsa



または~/.ssh/id_dsa



というファイルが存在する可能性が高くなります。 これはプライベート(別名プライベート)RSA / DSAキーであり、 ~/.ssh/id_rsa.pub



または~/.ssh/id_dsa.pub



はパブリック(別名パブリック)キーです。 ログインするサーバーの公開キーのコピーは~/.ssh/authorized_keys



になければなりません。 ログインしようとすると、sshクライアントは、デジタル署名を使用して秘密鍵があることを確認します。 サーバーは署名が有効であることと、公開キーが~/.ssh/authorized_keys



にあることを確認し、アクセスを取得します。



秘密鍵には何が保存されていますか?



暗号化されていない秘密鍵形式



パスワード(パスフレーズ)で秘密鍵を保護することをお勧めします。そうしないと、秘密鍵を盗み出した攻撃者が問題なくサーバーにログインできます。 最初に、暗号化されていないファイル形式を見てみましょうが、後で暗号化されたファイル形式を扱います。



暗号化されていないキーは次のようになります。



 -----BEGIN RSA PRIVATE KEY----- MIIEogIBAAKCAQEArCQG213utzqE5YVjTVF5exGRCkE9OuM7LCp/FOuPdoHrFUXk y2MQcwf29J3A4i8zxpES9RdSEU6iIEsow98wIi0x1/Lnfx6jG5Y0/iQsG1NRlNCC aydGvGaC+PwwWiwYRc7PtBgV4KOAVXMZdMB5nFRaekQ1ksdH/360KCGgljPtzTNl 09e97QBwHFIZ3ea5Eih/HireTrRSnvF+ywmwuxX4ubDr0ZeSceuF2S5WLXH2+TV0 ...    ... -----END RSA PRIVATE KEY-----
      
      





秘密鍵には、 ASN.1形式のデータが含まれており、 X.690標準に従ってバイトシーケンスとして提示され、Base64でエンコードされています。 大まかに言うと、ASN.1はJSONと比較できます(INTEGER、BOOLEAN、ツリー構造を形成できる文字列やシーケンスなど、さまざまなデータ型をサポートしています)。 ASN.1は、暗号化で広く使用されていますが、Webの出現により少し時代遅れになりました(理由はわかりません-それはかなりまともな形式のように見えます( おおよそ-Trans。



ssh-keygen



を使用してパスワードなしでテストRSAキーを生成し、 asn1parse



を使用してデコードしssh-keygen



(またはJavaScriptで記述されたASN.1デコーダーを使用します)。



 $ ssh-keygen -t rsa -N '' -f test_rsa_key $ openssl asn1parse -in test_rsa_key 0:d=0 hl=4 l=1189 cons: SEQUENCE 4:d=1 hl=2 l= 1 prim: INTEGER :00 7:d=1 hl=4 l= 257 prim: INTEGER :C36EB2429D429C7768AD9D879F98C... 268:d=1 hl=2 l= 3 prim: INTEGER :010001 273:d=1 hl=4 l= 257 prim: INTEGER :A27759F60AEA1F4D1D56878901E27... 534:d=1 hl=3 l= 129 prim: INTEGER :F9D23EF31A387694F03AD0D050265... 666:d=1 hl=3 l= 129 prim: INTEGER :C84415C26A468934F1037F99B6D14... 798:d=1 hl=3 l= 129 prim: INTEGER :D0ACED4635B5CA5FB896F88BB9177... 930:d=1 hl=3 l= 128 prim: INTEGER :511810DF9AFD590E11126397310A6... 1061:d=1 hl=3 l= 129 prim: INTEGER :E3A296AE14E7CAF32F7E493FDF474...
      
      





ASN.1のデータ構造は非常に単純です。9つの整数のシーケンスです。 それらの目的はRFC2313で定義されています 。 1番目と3番目の番号は、バージョン番号(0)と公開指数eです。 2番目と4番目の数字(2048ビット長)は、モジュールnと秘密指数dです。 これらの番号はRSAキーパラメータです。 残りの5つは、 ndを知ることで取得できます。これらは、いくつかの操作を高速化するためにファイルにキャッシュされます。



DSAキーの構造は類似しており、6つの数字が含まれています。



 $ ssh-keygen -t dsa -N '' -f test_dsa_key $ openssl asn1parse -in test_dsa_key 0:d=0 hl=4 l= 444 cons: SEQUENCE 4:d=1 hl=2 l= 1 prim: INTEGER :00 7:d=1 hl=3 l= 129 prim: INTEGER :E497DFBFB5610906D18BCFB4C3CCD... 139:d=1 hl=2 l= 21 prim: INTEGER :CF2478A96A941FB440C38A86F22CF... 162:d=1 hl=3 l= 129 prim: INTEGER :83218C0CA49BA8F11BE40EE1A7C72... 294:d=1 hl=3 l= 128 prim: INTEGER :16953EA4012988E914B466B9C37CB... 425:d=1 hl=2 l= 21 prim: INTEGER :89A356E922688EDEB1D388258C825...
      
      





パスワードで保護された秘密キーの形式



ここで、秘密キーを盗むことができる潜在的な攻撃者の生活を複雑にします。パスワードで保護します。 ファイルはどうなりましたか?



 $ ssh-keygen -t rsa -N 'super secret passphrase' -f test_rsa_key $ cat test_rsa_key -----BEGIN RSA PRIVATE KEY----- Proc-Type: 4,ENCRYPTED DEK-Info: AES-128-CBC,D54228DB5838E32589695E83A22595C7 3+Mz0A4wqbMuyzrvBIHx1HNc2ZUZU2cPPRagDc3M+rv+XnGJ6PpThbOeMawz4Cbu lQX/Ahbx+UadJZOFrTx8aEWyZoI0ltBh9O5+ODov+vc25Hia3jtayE51McVWwSXg wYeg2L6U7iZBk78yg+sIKFVijxiWnpA7W2dj2B9QV0X3ILQPxbU/cRAVTd7AVrKT ...    ... -----END RSA PRIVATE KEY-----
      
      





2つのヘッダー行が追加されており、Base64文字列のデコード結果は有効なASN.1ではなくなっていることに注意してください。 事実は、ASN.1の構造です。 暗号化。 ヘッダーから、どのアルゴリズムが暗号化に使用されたかがわかります:CBCモードのAES-128DEK-Info



ヘッダーの128ビットの16進数文字列は、初期化ベクトル(IV)です。 ここで異常なことは何もありません。すべての一般的な暗号化ライブラリは、ここで使用されるアルゴリズムで動作します。



しかし、AESキーはパスワードからどのように取得されますか? 私はドキュメントでこれを見つけられなかったので、OpenSSLのソースコードを理解することを余儀なくされました。 暗号化キーの取得について私が知ったのは次のとおりです。



  1. 初期化ベクトルの最初の8バイトがパスワードに追加されます(実際、これらはソルトです)。
  2. MD5ハッシュは、受信した文字列から1回取得されます。


確認するために、 DEK-Info



ヘッダーから初期化ベクトルを取得して秘密鍵を復号化します。



 $ tail -n +4 test_rsa_key | grep -v 'END ' | base64 -d | openssl aes-128-cbc -d -iv D54228DB5838E32589695E83A22595C7 -K $( ruby -rdigest/md5 -e 'puts Digest::MD5.hexdigest(["super secret passphrase",0xD5,0x42,0x28,0xDB,0x58,0x38,0xE3,0x25].pack("a*cccccccc"))' ) | openssl asn1parse -inform DER
      
      





このコマンドは、RSAキーパラメータを出力します。 キーを見たいだけなら、もっと簡単な方法があります:



 $ openssl rsa -text -in test_rsa_key -passin 'pass:super secret passphrase'
      
      





しかし、2つの脆弱性に注意を払うために、パスワードからAESキーがどのように取得されるかを正確に示したかったのです。



  1. MD5の使用はコードに登録されています。つまり、形式を変更しないと、別のハッシュ関数(SHA-1など)に切り替えることができません。 MD5が十分に安全でないことが判明した場合、問題が発生します。 ( 実際にはいいえ、 コメントを参照してください-約。
  2. ハッシュ関数は一度だけ適用されます。 MD5とAESは計算が速いため、短いパスワードは簡単に推測できます。


たとえば、誰かがラップトップまたはハードドライブをバックアップで盗むなど、sshキーが悪意のある人に渡されると、攻撃者は計算能力がほとんどなくても多数のパスワードを試すことができます。 辞書のパスワードを設定すると、数秒でそれを取得できます。



これは悪いニュースです。キーのパスワード保護は、予想したほど良くありません。 しかし、良いニュースがあります。より安全な秘密キー形式に切り替えることができます。



PKCS#8を使用したキーセキュリティの強化



そのため、パスワードから対称暗号化キーを取得するアルゴリズムが必要になりますが、これは動作が遅いため、攻撃者はパスワードを推測するためにより多くの計算時間を必要としました。



sshキーの扱いにくい名前にはいくつかの標準があります。





ssh-keygen



が従来の形式でキーを生成する理由はわかりませんが、長年にわたってより良い代替手段が存在していました。 サーバーソフトウェアとの互換性に関する問題ではありません。秘密キーがコンピューターから離れることはありません。 幸いなことに、既存のキーはPKCS#8への変換がかなり簡単です。



 $ mv test_rsa_key test_rsa_key.old $ openssl pkcs8 -topk8 -v2 des3 \ -in test_rsa_key.old -passin 'pass:super secret passphrase' \ -out test_rsa_key -passout 'pass:super secret passphrase'
      
      





PKCS#8形式の新しいキーファイルを使用しようとすると、すべてが以前と同じように機能することがあります。 今、ファイルの中に何があるか見てみましょう。



 $ cat test_rsa_key -----BEGIN ENCRYPTED PRIVATE KEY----- MIIFDjBABgkqhkiG9w0BBQ0wMzAbBgkqhkiG9w0BBQwwDgQIOu/S2/v547MCAggA MBQGCCqGSIb3DQMHBAh4q+o4ELaHnwSCBMjA+ho9K816gN1h9MAof4stq0akPoO0 CNvXdtqLudIxBq0dNxX0AxvEW6exWxz45bUdLOjQ5miO6Bko0lFoNUrOeOo/Gq4H dMyI7Ot1vL9UvZRqLNj51cj/7B/bmfa4msfJXeuFs8jMtDz9J19k6uuCLUGlJscP ... - ... -----END ENCRYPTED PRIVATE KEY-----
      
      





最初と最後の行が変更され( BEGIN ENCRYPTED PRIVATE KEY



BEGIN RSA PRIVATE KEY



代わりにBEGIN RSA PRIVATE KEY



)、 Proc-Type



およびDEK-Info



ヘッダーが消えていることに注意してください。 実際、ファイルは同じASN.1形式でデータを保存します。



 $ openssl asn1parse -in test_rsa_key 0:d=0 hl=4 l=1294 cons: SEQUENCE 4:d=1 hl=2 l= 64 cons: SEQUENCE 6:d=2 hl=2 l= 9 prim: OBJECT :PBES2 17:d=2 hl=2 l= 51 cons: SEQUENCE 19:d=3 hl=2 l= 27 cons: SEQUENCE 21:d=4 hl=2 l= 9 prim: OBJECT :PBKDF2 32:d=4 hl=2 l= 14 cons: SEQUENCE 34:d=5 hl=2 l= 8 prim: OCTET STRING [HEX DUMP]:3AEFD2DBFBF9E3B3 44:d=5 hl=2 l= 2 prim: INTEGER :0800 48:d=3 hl=2 l= 20 cons: SEQUENCE 50:d=4 hl=2 l= 8 prim: OBJECT :des-ede3-cbc 60:d=4 hl=2 l= 8 prim: OCTET STRING [HEX DUMP]:78ABEA3810B6879F 70:d=1 hl=4 l=1224 prim: OCTET STRING [HEX DUMP]:C0FA1A3D2BCD7A80DD61F4C0287F8B2D...
      
      





JavaScriptデコーダーを使用して、ASN.1の構造を調べます



 Sequence (2 elements) |- Sequence (2 elements) | |- Object identifier: 1.2.840.113549.1.5.13 // using PBES2 from PKCS#5 | `- Sequence (2 elements) | |- Sequence (2 elements) | | |- Object identifier: 1.2.840.113549.1.5.12 // using PBKDF2 — yay! :) | | `- Sequence (2 elements) | | |- Byte string (8 bytes): 3AEFD2DBFBF9E3B3 // salt | | `- Integer: 2048 // iteration count | `- Sequence (2 elements) | Object identifier: 1.2.840.113549.3.7 // encrypted with Triple DES, CBC | Byte string (8 bytes): 78ABEA3810B6879F // initialization vector `- Byte string (1224 bytes): C0FA1A3D2BCD7A80DD61F4C0287F8B2DAB46A43E... // encrypted key blob
      
      





OID(オブジェクト識別子)-グローバルに一意のデジタル識別子を指します。 それらから、暗号化スキームpkcs5PBES2 、キー取得関数PBKDF2および暗号化アルゴリズムdes-ede3-cbcが使用されていることがわかります。 ハッシュ関数は明示的に指定されていません。つまり、 デフォルトhMAC-SHA1が使用されます



OIDをファイルに保存すると、コンテナ形式を変更せずにキーを更新できるため、優れています(たとえば、より優れた暗号化アルゴリズムが発明された場合)。



また、暗号化キーを取得するプロセスで、2048回の反復が実行されることもわかります。 これは、従来のssh-key形式を使用するときにハッシュ関数を1回使用するよりもはるかに優れています。パスワードの列挙には時間がかかります。 現在、反復の数はOpenSSLコードに登録されています。将来設定できるようになると思います。



おわりに



秘密鍵に複雑なパスワードを設定する場合、従来の形式からPKCS#8に変換することは、パスワードの長さを数文字増やすことと比較できます。 弱いパスワードを使用する場合、PKCS#8はその選択をはるかに困難にします。



キー形式の変更は非常に簡単です。



 $ mv ~/.ssh/id_rsa ~/.ssh/id_rsa.old $ openssl pkcs8 -topk8 -v2 des3 -in ~/.ssh/id_rsa.old -out ~/.ssh/id_rsa $ chmod 600 ~/.ssh/id_rsa # ,    .  ,    $ rm ~/.ssh/id_rsa.old
      
      





openssl pkcs8



は、3回パスワードを要求しますopenssl pkcs8



回は既存のキーをロック解除するため、もう1回は新しいキーファイルを作成するためです。 新しいパスワードを考え出すことも、古いパスワードを使用することもできますが、重要ではありません。



すべてのソフトウェアがPKCS#8形式を読み取ることができるわけではありませんが、心配することはありません-sshクライアントのみがプライベートsshキーにアクセスする必要があります。 サーバーの観点から見ると、秘密鍵を別の形式で保存しても何も変わりません。



翻訳者はコメントや建設的な批判を喜んで聞きます。



All Articles